diff --git a/.gitignore b/.gitignore index 4307a5182..5c0ef4d1f 100644 --- a/.gitignore +++ b/.gitignore @@ -24,10 +24,16 @@ vic_headers.py *.mod vicNl -vic_classic -vic_image +vic_classic.exe +vic_image.exe liblnd.a _vic.py +vic_timing_*txt +massif.out.* +vic_call_graph_* + +vic_call_graph* +gmon.out .DS_Store TAGS @@ -35,5 +41,6 @@ TAGS *egg-info* # Python stuff +__pycache__ .ipynb_checkpoints .eggs diff --git a/.travis.yml b/.travis.yml index 53c1ccc06..59d8a85e8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,6 +13,8 @@ matrix: addons: apt_packages: - libnetcdf-dev + - netcdf-bin + - netcdf-doc - gfortran - valgrind # open-mpi is built from source in image.travis @@ -23,6 +25,8 @@ matrix: addons: apt_packages: - libnetcdf-dev + - netcdf-bin + - netcdf-doc - gfortran # open-mpi is built from source in cesm.travis # Classic Driver diff --git a/ci/cesm.travis b/ci/cesm.travis index d45838e42..c14f8a1c1 100644 --- a/ci/cesm.travis +++ b/ci/cesm.travis @@ -1,7 +1,5 @@ #!/usr/bin/env bash -set -e - DRIVER_PATH="./vic/drivers/cesm/" DRIVER_LIB="${DRIVER_PATH}lndlib.a" diff --git a/ci/classic.travis b/ci/classic.travis index 300359712..09295358d 100644 --- a/ci/classic.travis +++ b/ci/classic.travis @@ -1,7 +1,5 @@ #!/usr/bin/env bash -set -e - DRIVER_PATH="${TRAVIS_BUILD_DIR}/vic/drivers/classic/" DRIVER_EXE="${DRIVER_PATH}vic_classic.exe" SAMPLES_PATH="${TRAVIS_BUILD_DIR}/samples/" @@ -59,10 +57,8 @@ function vic_script { # Run test package ./tests/run_tests.py unit examples system \ - --vic_exe=${DRIVER_EXE} \ - --driver=${TESTID} \ + --classic=${DRIVER_EXE} \ --data_dir=${SAMPLES_PATH}/data - } function vic_after_success { diff --git a/ci/image.travis b/ci/image.travis index afca7fb22..c0d022fe2 100644 --- a/ci/image.travis +++ b/ci/image.travis @@ -1,7 +1,5 @@ #!/usr/bin/env bash -set -e - DRIVER_PATH="${TRAVIS_BUILD_DIR}/vic/drivers/image/" DRIVER_EXE="${DRIVER_PATH}vic_image.exe" SAMPLES_PATH="${TRAVIS_BUILD_DIR}/samples/" @@ -34,11 +32,13 @@ function vic_before_install { brew install homebrew/versions/gcc49 brew reinstall open-mpi --build-from-source fi - export TRAVIS_NETCDFPATH=/usr/local - export TRAVIS_MPIPATH=/usr/local + export TRAVIS_MPIPATH=/usr/local + export NC_LIBS=$(/usr/local/bin/nc-config --libs) + export NC_CFLAGS=$(/usr/local/bin/nc-config --cflags) else - export TRAVIS_NETCDFPATH=/usr export TRAVIS_MPIPATH="${HOME}/mpich" + export NC_LIBS=$(/usr/bin/nc-config --libs) + export NC_CFLAGS=$(/usr/bin/nc-config --cflags) # Install MPICH if [ ! -d ${TRAVIS_MPIPATH} ]; then @@ -46,11 +46,15 @@ function vic_before_install { else echo "MPICH installed..." fi - find ${TRAVIS_MPIPATH} -name mpiexec - find ${TRAVIS_MPIPATH} -name mpicc fi - export PATH=$TRAVIS_MPIPATH/bin:$PATH + export PATH=${TRAVIS_MPIPATH}/bin:${PATH} + find ${TRAVIS_MPIPATH} -name mpiexec mpiexec --version + find ${TRAVIS_MPIPATH} -name mpicc + export MPICC="${TRAVIS_MPIPATH}/bin/mpicc" + $MPICC --version + echo "NETCDF LIB FLAGS ------> ${NC_LIBS}" + echo "NETCDF C FLAGS --------> ${NC_CFLAGS}" } function vic_install { @@ -79,10 +83,8 @@ function vic_script { # Run test package ./tests/run_tests.py unit examples system \ - --vic_exe=${DRIVER_EXE} \ - --driver=${TESTID} \ + --image=${DRIVER_EXE} \ --data_dir=${SAMPLES_PATH}/data - } function vic_after_success { diff --git a/ci/requirements.yml b/ci/requirements.yml index 6e97212fb..a7caa8514 100644 --- a/ci/requirements.yml +++ b/ci/requirements.yml @@ -10,8 +10,12 @@ dependencies: - configobj - xarray - bottleneck + - matplotlib + - seaborn + - psutil - pip: - git+https://github.com/UW-Hydro/tonic.git - engarde - pytest-faulthandler - pytest-xdist + - gprof2dot diff --git a/ci/vic_install_utils b/ci/vic_install_utils index 12ac5d84e..0683f571c 100644 --- a/ci/vic_install_utils +++ b/ci/vic_install_utils @@ -1,16 +1,16 @@ #!/usr/bin/env bash -set -e - if [ -z "$WORKDIR" ]; then export WORKDIR=$HOME/workdir mkdir -p $WORKDIR fi +export VIC_VALGRIND_SUPPRESSIONS=${TRAVIS_BUILD_DIR}/tests/vic_valgrind_suppressions.supp + function install_mpich { - wget --no-check-certificate -q http://www.mpich.org/static/downloads/3.2/mpich-3.2.tar.gz - tar -xzf mpich-3.2.tar.gz - cd mpich-3.2 + wget --no-check-certificate -q http://www.mpich.org/static/downloads/3.1b1/mpich-3.1b1.tar.gz + tar -xzf mpich-3.1b1.tar.gz + cd mpich-3.1b1 mkdir build && cd build ../configure CC=$CC CXX=$CXX --prefix=${TRAVIS_MPIPATH} make -j2 diff --git a/docs/Development/ReleaseNotes.md b/docs/Development/ReleaseNotes.md index 4cd893a77..5839ba9ff 100644 --- a/docs/Development/ReleaseNotes.md +++ b/docs/Development/ReleaseNotes.md @@ -15,9 +15,9 @@ For VIC 5 and later, type `vic _{classic,image}.exe -v` ------------------------------ -## VIC 5.0.0 (Release Candidate 1) +## VIC 5.0.0 (Release Candidate 2) -**Release date: June 28, 2016** +**Release date: August 18, 2016** This is a major update from VIC 4. The VIC 5.0.0 release aims to have nearly identical physics as VIC 4.2 while providing a clean, refactored code base supporting multiple drivers. There are a number of new features, bug fixes, and backward incompatible changes. See the VIC Github page for more details on the changes included in this release. @@ -60,19 +60,22 @@ This is a major update from VIC 4. The VIC 5.0.0 release aims to have nearly ide 9. Tests Datasets ([GH#79](https://github.com/UW-Hydro/VIC/issues/79)) - **TODO:** See https://github.com/UW-Hydro/VIC/issues/79 for more information. + See https://github.com/UW-Hydro/VIC/issues/79 for more information. A temporary location of the test data is here: ftp://ftp.hydro.washington.edu/pub/gergel/VIC5_test_data/ 10. Testing and Continuous Integration ([GH#190](https://github.com/UW-Hydro/VIC/pull/190)) A comprehensive testing platform has been implemented and is available for public use along with the VIC model. A small subset of the test platform is run on [Travis-CI](https://travis-ci.org/UW-Hydro/VIC), which facilitates continuous integration of the VIC test platform. More information on the test platform is [here](Testing.md) +11. Run-time profiling and timing ([GH#442](https://github.com/UW-Hydro/VIC/pull/442)) + + A timing module has been added to VIC in order to assess the computational cost and throughput of the VIC model. New output variables (`OUT_TIME_VICRUN_WALL` and `OUT_TIME_VICRUN_CPU`) document the time spent in `vic_run` for each variable. Additionally, a timing table is printed to `LOG_DEST` at the end of each simulation. + #### Backwards Incompatible Changes: 1. Classic Driver I/O Formatting ([GH#18](https://github.com/UW-Hydro/VIC/issues/18), [GH#204](https://github.com/UW-Hydro/VIC/issues/204), [GH#227](https://github.com/UW-Hydro/VIC/pull/227)) The format of ASCII forcing and output files has changed in VIC 5. These changes were motivated by the desire to improve simulation metadata tracking and reproducibility of VIC simulations. - - Forcing files now require date stamps for each timestep and a header specifies the names of the forcing variables. - Output files now include a header with simulation metadata and variable names. The `PRT_HEADER` option has been deprecated. 2. Classic Driver Global Parameter Options @@ -155,18 +158,26 @@ This is a major update from VIC 4. The VIC 5.0.0 release aims to have nearly ide 3. Fix related to exact restart ([GH#481](https://github.com/UW-Hydro/VIC/pull/481), [GH#507](https://github.com/UW-Hydro/VIC/pull/507), [GH#509](https://github.com/UW-Hydro/VIC/pull/509)) - Previously, VIC did not produce the same results (fluxes and states) if a simulation is separated into multiple shorter-period runs by saving the state variables and restarting. This was due to: 1) the MTCLIM algorithm resulted in slightly different sub-daily meteorological variable values for different length of forcing (MTCLIM is deprecated in the current version); 2) a few bugs resulting in inexact restart. The following bugs have been fixed: + Previously, VIC did not produce the same results (fluxes and states) if a simulation was separated into multiple shorter-period runs by saving the state variables and restarting. This was due to: + 1. The MTCLIM algorithm resulted in slightly different sub-daily meteorological variable values for different lengths of forcings (MTCLIM is deprecated in the current version) + 2. A few bugs resulting in inexact restart. + + The following bugs have been fixed: - The prognostic state variable `energy.Tfoliage` (foliage temperature) is now saved to the state file - Two flux variables `energy.LongUnderOut` and `energy.snow_flux` are now saved to the state file. !!!Note - This is a temporary solution to ensure exact restart. A better way of handling the two flux variables needs to be done in the future (see [GH#479](https://github.com/UW-Hydro/VIC/issues/479)) + This is a temporary solution to ensure exact restart. A better way of handling these two flux variables needs to be done in the future (see [GH#479](https://github.com/UW-Hydro/VIC/issues/479)) 4. Fix for binary state file I/O ([GH#487](https://github.com/UW-Hydro/VIC/pull/487)) Fixed a bug so that the binary format state file I/O works correctly. +5. Fix for a physical constant (water heat capacity) ([GH#574](https://github.com/UW-Hydro/VIC/pull/574)) + + Fixed a bug where volumetric heat capacity of water should be used in `func_canopy_energy_bal` (previously specific heat capacity was used). + ------------------------------ ## VIC 4.2.c [![DOI](https://zenodo.org/badge/doi/10.5281/zenodo.35302.svg)](http://dx.doi.org/10.5281/zenodo.35302) diff --git a/docs/Documentation/Drivers/Image/RunVIC.md b/docs/Documentation/Drivers/Image/RunVIC.md index 05b2b9b44..e877ea571 100644 --- a/docs/Documentation/Drivers/Image/RunVIC.md +++ b/docs/Documentation/Drivers/Image/RunVIC.md @@ -1,7 +1,7 @@ # Running the VIC Image Driver ## Dependencies: -The Image Driver's has three dependencies: +The Image Driver has three dependencies: 1. A C compiler. We routinely test VIC using the following compilers: @@ -22,9 +22,16 @@ The Image Driver's has three dependencies: ## Compiling In most cases, you will need to edit the `NETCDF_PATH` and `MPI_PATH` variables in the `Makefile`. -If you want to use a compiler other than `gcc`, either edit the Makefile or set the `CC` environment variable, e.g. +If you want to use a compiler other than `mpicc`, either edit the Makefile or set the `MPICC` environment variable, e.g. - export CC=icc + MPICC=/path/to/mpi_c_compiler + +The flags and libraries required to compile VIC with netCDF are automatically determined in the `Makefile`. They can be overwritten by setting the following two environment variables. These variables can be determined by running `nc-config --all`. + + NC_LIBS="-L/path/to/libs ..." + NC_CFLAGS="-I/path/to/includes -your_c_flags ..." + +In some versions of the MPI library (e.g. OPEN-MPI with Intel), you may also need to set the environment variable `MX_RCACHE=2` prior to compiling. - Change directory, `cd`, to the "Image Driver" source code directory and type `make` @@ -37,10 +44,16 @@ If you want to use a compiler other than `gcc`, either edit the Makefile or set At the command prompt, type: -`vic_image.exe -g global_parameter_filename` +`vic_image.exe -g global_parameter_filename.txt` where `global_parameter_filename` = name of the global parameter file corresponding to your project. +To run VIC image driver using multiple processor, type the following instead: + +`mpiexec -np n_proc vic_image.exe -g global_parameter_filename.txt` + +where `n_proc` = number of processors to be used + ## Other Command Line Options VIC has a few other command line options: diff --git a/samples/global.param.sample.image.txt b/samples/global.param.sample.image.txt index 0c0402e53..8dada2d87 100644 --- a/samples/global.param.sample.image.txt +++ b/samples/global.param.sample.image.txt @@ -139,22 +139,19 @@ WIND_H 10.0 # height of wind speed measurement (m) ####################################################################### # Land Surface Files and Parameters ####################################################################### -SOIL (put the soil parameter path/file here) # Soil parameter path/file +PARAMETERS (put the parameters path/file here) # Parameters path/file BASEFLOW ARNO # ARNO = columns 5-8 are the standard VIC baseflow parameters; NIJSSEN2001 = columns 5-8 of soil file are baseflow parameters from Nijssen et al (2001) JULY_TAVG_SUPPLIED FALSE # TRUE = final column of the soil parameter file will contain average July air temperature, for computing treeline; this will be ignored if COMPUTE_TREELINE is FALSE; FALSE = compute the treeline based on the average July air temperature of the forcings over the simulation period ORGANIC_FRACT FALSE # TRUE = simulate organic soils; soil param file contains 3*Nlayer extra columns, listing for each layer the organic fraction, and the bulk density and soil particle density of the organic matter in the soil layer; FALSE = soil param file does not contain any information about organic soil, and organic fraction should be assumed to be 0 -VEGPARAM (put the veg parameter path/file here) # Veg parameter path/file -ROOT_ZONES 3 # Number of root zones (must match format of veg param file) #LAI_SRC FROM_VEGPARAM # FROM_VEGPARAM = read LAI from veg param file; FROM_VEGHIST = read LAI from veg_hist forcing file #ALB_SRC FROM_VEGPARAM # FROM_VEGPARAM = read ALBEDO from veg param file; FROM_VEGHIST = read ALBEDO from veg_hist forcing file #FCAN_SRC FROM_DEFAULT # FROM_DEFAULT = set all fcanopy values to 1.0; FROM_VEGPARAM = read FCANOPY from veg param file; FROM_VEGHIST = read FCANOPY from veg_hist forcing file -SNOW_BAND 1 # Number of snow bands; if number of snow bands > 1, you must insert the snow band path/file after the number of bands (e.g. SNOW_BAND 5 my_path/my_snow_band_file) +SNOW_BAND TRUE # TRUE if VIC should use snowbands in PARAMETERS file, else FALSE. ####################################################################### # Lake Simulation Parameters -# These need to be un-commented and set to correct values only when running lake model (LAKES is not FALSE) ####################################################################### -#LAKES (put lake parameter path/file here) # Lake parameter path/file +#LAKES TRUE # TRUE if VIC should use the lake module, else FALSE. Requires lake parameters in the PARAMETERS file. #LAKE_PROFILE FALSE # TRUE = User-specified depth-area parameters in lake parameter file; FALSE = VIC computes a parabolic depth-area profile ####################################################################### diff --git a/tests/examples/domain.stehekin.20151028.nc b/tests/examples/domain.stehekin.20151028.nc deleted file mode 100644 index 5d8f1fa37..000000000 Binary files a/tests/examples/domain.stehekin.20151028.nc and /dev/null differ diff --git a/tests/examples/global_param.image.STEHE.feb.txt b/tests/examples/global_param.image.STEHE.feb.txt index e9c55f04d..6e0ade4ae 100644 --- a/tests/examples/global_param.image.STEHE.feb.txt +++ b/tests/examples/global_param.image.STEHE.feb.txt @@ -15,7 +15,7 @@ FROZEN_SOIL FALSE AERO_RESIST_CANSNOW AR_406 -DOMAIN $test_data_dir/image/Stehekin/parameters/domain.stehekin.20151028.nc +DOMAIN $test_data_dir/image/Stehekin/parameters/domain.stehekin.20151028.nc DOMAIN_TYPE LAT lat DOMAIN_TYPE LON lon DOMAIN_TYPE MASK mask @@ -24,22 +24,19 @@ DOMAIN_TYPE FRAC frac DOMAIN_TYPE YDIM lat DOMAIN_TYPE XDIM lon -FORCING1 $test_data_dir/image/Stehekin/forcings/Stehekin_image_test.forcings_10days. +FORCING1 $test_data_dir/image/Stehekin/forcings/Stehekin_image_test.forcings_10days. FORCE_TYPE AIR_TEMP tas FORCE_TYPE PREC prcp FORCE_TYPE PRESSURE pres FORCE_TYPE SWDOWN dswrf -FORCE_TYPE LWDOWN dlwrf +FORCE_TYPE LWDOWN dlwrf FORCE_TYPE VP shum FORCE_TYPE WIND wind WIND_H 10.0 -SOIL $test_data_dir/image/Stehekin/parameters/Stehekin_test_params_20160327.nc +PARAMETERS $test_data_dir/image/Stehekin/parameters/Stehekin_test_params_20160327.nc NODES 3 -VEGPARAM $test_data_dir/image/Stehekin/parameters/Stehekin_test_params_20160327.nc -ROOT_ZONES 3 -VEGLIB $test_data_dir/image/Stehekin/parameters/Stehekin_test_params_20160327.nc -SNOW_BAND 1 $test_data_dir/image/Stehekin/parameters/Stehekin_test_params_20160327.nc +SNOW_BAND TRUE RESULT_DIR $result_dir diff --git a/tests/profiling/README.md b/tests/profiling/README.md new file mode 100644 index 000000000..ffa974bba --- /dev/null +++ b/tests/profiling/README.md @@ -0,0 +1,4 @@ +VIC Profiling Tests +======= + +These tests quantify the performance of VIC in terms of CPU/wall time and memory usage. diff --git a/tests/profiling/run_gprof.bash b/tests/profiling/run_gprof.bash new file mode 100755 index 000000000..a813acd37 --- /dev/null +++ b/tests/profiling/run_gprof.bash @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +set -e +# set -x + +# A POSIX variable +OPTIND=1 # Reset in case getopts has been used previously in the shell. + +# Initialize our own variables: +vic_exe="" +vic_global="" + +function usage { + echo "Usage: `basename $0` -e vic_executable -g vic_global -h for help"; +} + + +while getopts "h?e::g::" opt; do + case "$opt" in + h|\?) + usage + exit 0 + ;; + e) vic_exe=$OPTARG ;; + g) vic_global=$OPTARG ;; + esac +done + +shift $((OPTIND-1)) + +[ "$1" = "--" ] && shift + +GIT_VERSION=$(git describe --abbrev=4 --dirty --always --tags) + +echo "----------------------- START VIC GPROF PROFILING -----------------------" +echo "" +echo "Date : $(date)" +echo "Machine : $HOSTNAME" +echo "User : $USER" +echo "VIC Test Git Version : $GIT_VERSION" +echo "VIC Executable : $vic_exe" +echo "VIC Global Parameter File : $vic_global" +echo "" + +echo "VIC Executable Version Info" +echo "---------------------------" +$vic_exe -v +echo "" + +$vic_exe -g $vic_global 2>&1 | tail -n 55 + +now=`date +"%y%m%d"` +gprof $vic_exe | gprof2dot | dot -Tpng -o vic_call_graph_$now.png + +echo "" +echo "------------------------ END VIC GPROF PROFILING ------------------------" +exit 0 + +# End of file diff --git a/tests/run_profiling.py b/tests/run_profiling.py new file mode 100755 index 000000000..796fe3be9 --- /dev/null +++ b/tests/run_profiling.py @@ -0,0 +1,221 @@ +#!/usr/bin/env python +'''VIC testing command line interface''' + +from __future__ import print_function +import os +import argparse +from collections import namedtuple +import psutil +import string +import subprocess +from subprocess import check_call +import datetime +import getpass +import socket +import time + +import numpy as np + +from tonic.models.vic.vic import VIC + +host_config = namedtuple('host_config', + ('max_cores', 'template', 'submit', 'mpiexec')) + +table_header = '''----------------- START VIC SCALING PROFILE ----------------- + +Date : $date +Machine : $hostname +User : $user +VIC Test Git Version : $git_version +VIC Executable : $vic_exe +VIC Global Parameter File : $vic_global +Test Maximum MPI Cores : $max_cores + +VIC Executable Version Info +--------------------------- +$vic_version +Cores | Time (Seconds) +---------------------- +''' + + +hosts = { + 'local': host_config(max_cores=psutil.cpu_count(), submit=None, template=None, + mpiexec=os.getenv('MPIEXEC', 'mpiexec')), + 'hydra': host_config(max_cores=64, submit='qsub', mpiexec='mpiexec', + template='''#!/bin/bash +# +#$ -N VIC_scaling_test_$np +#$ -cwd +#$ -j y +#$ -S /bin/bash +#$ -m be +#$ -pe orte $np + +# Qsub template for UW's Hydra Cluster +# Scheduler: SGE +# Valid values for np 1-64 +if [ "$np" -gt "64" ] + echo "$np exceeds maximum number of processes on Hydra" + exit 1 +fi + +START=$(date +%s) +mpiexec -np $np $vic_exe -g $vic_global +END=$(date +%s) +DIFF=$(echo "$END - $START" | bc) +printf "%5s | %f" $np $DIFF >> $timing_table_file'''), + } + +OUTPUT_WIDTH = 100 + +description = ''' + VIC Test Suite +------------------------------------------------------------------------------- +This is the VIC Profiling Test Suite. There are 2 main test types: + + 1. Gprof Profiling: This test will generate a profiling call graph using + gprof. This test requires building your VIC executable with the flags `-pg`. + 2. Scaling: This test will generate a MPI scaling timing table. +------------------------------------------------------------------------------- +''' + +epilog = ''' +------------------------------------------------------------------------------- +For questions about the development or use of VIC or use of this test module, +please email the VIC users list serve at vic_users@u.washington.edu. +------------------------------------------------------------------------------- +''' + + +class CustomFormatter(argparse.ArgumentDefaultsHelpFormatter, + argparse.RawDescriptionHelpFormatter): + pass + + +def main(): + ''' ''' + # dates and times + starttime = datetime.datetime.now() + ymd = starttime.strftime('%Y%m%d') + + # parse command line options + parser = argparse.ArgumentParser(description=description, epilog=epilog, + formatter_class=CustomFormatter) + + parser.add_argument('vic_exe', type=str, + help='VIC executable to test') + parser.add_argument('--kind', type=str, + help='Specify which type of test should be run', + choices=['scaling', 'profile'], + default='scaling') + parser.add_argument('--host', type=str, + help='Host machine to run test on, if not specified, ' + 'test will be run locally', + choices=list(hosts.keys()), + default='local') + parser.add_argument('--global_param', '-g', type=str, + help='global parameter file to test') + parser.add_argument('--timing', '-t', type=str, + default='vic_timing_{}.txt'.format(ymd), + help='path to timing file') + parser.add_argument('--clean', action='store_true', help='Clean up run files') + parser.add_argument('--test', action='store_true', + help='Test the setup but do not run VIC') + + args = parser.parse_args() + + if args.global_param is None: + raise ValueError('Global Parameter option is required') + + if args.kind == 'scaling': + run_scaling(args) + elif args.kind == 'profile': + run_profiling(args) + else: + raise ValueError('Unknown test kind %s' % args.kind) + + +def run_profiling(args): + '''wrapper function for profiling tests''' + cmd = './profiling/run_gprof.bash -e {vic_exe} -g {vic_global}'.format( + vic_exe=args.vic_exe, vic_global=args.global_param) + check_call(cmd, shell=True) + + +def run_scaling(args): + '''wrapper function for scaling tests''' + config = hosts[args.host] + vic_exe = VIC(args.vic_exe) + + # write timing file header + header = string.Template(table_header) + header_kwargs = get_header_info(args.vic_exe, args.global_param, + config.max_cores) + header = header.safe_substitute(**header_kwargs) + with open(args.timing, 'w') as f: + f.write(header) + + for n in log2_range(config.max_cores).astype(np.int): + if config.template: + # run on a cluster of some kind + # start by printing the template + print('-'.ljust(OUTPUT_WIDTH, '-')) + print('{host} template'.format(host=args.host).center(OUTPUT_WIDTH)) + print('-'.ljust(OUTPUT_WIDTH, '-')) + print(config.template) + print('-'.ljust(OUTPUT_WIDTH, '-')) + template = string.Template(config.template) + + run_string = template.safe_substitute(np=n, vic_exe=args.vic_exe, + vic_global=args.global_param, + timing_table_file=args.timing) + run_file = 'vic_{host}_{n}.sh'.format(host=args.host, n=n) + with open(run_file, 'w') as f: + f.write(run_string) + + cmd = '{submit} {run_file}'.format(submit=config.submit, + run_file=run_file) + print(cmd) + if not args.test: + check_call(cmd, shell=True) + + if args.clean: + os.remove(run_file) + else: + # run locally + print('Running {} with {} processors'.format(args.vic_exe, n)) + if not args.test: + start = time.time() + vic_exe.run(args.global_param, mpi_proc=int(n)) + end = time.time() + diff = end - start + with open(args.timing, 'a') as f: + f.write('%5s | %.2f\n' % (n, diff)) + + print('See %s for scaling table' % args.timing) + + +def log2_range(m): + '''make an array of integers that increase by 2^n with maximum value of m''' + n = int(np.floor(np.log2(m))) + 1 + return np.exp2(np.arange(n)).astype(np.int) + + +def get_header_info(vic_exe, vic_global, max_cores): + '''get info for timing table headers''' + header_kwargs = {} + header_kwargs['date'] = datetime.datetime.now() + header_kwargs['hostname'] = socket.gethostname() + header_kwargs['user'] = getpass.getuser() + header_kwargs['git_version'] = subprocess.check_output( + ['git', 'describe', '--abbrev=4', '--dirty', '--always', '--tags']).decode() + header_kwargs['vic_exe'] = vic_exe + header_kwargs['vic_global'] = vic_global + header_kwargs['max_cores'] = max_cores + header_kwargs['vic_version'] = subprocess.check_output([vic_exe, '-v']).decode() + return header_kwargs + + +if __name__ == '__main__': + main() diff --git a/tests/run_tests.py b/tests/run_tests.py index 31faba85a..5672c0ba3 100755 --- a/tests/run_tests.py +++ b/tests/run_tests.py @@ -13,18 +13,24 @@ import pytest -from tonic.models.vic.vic import VIC +from tonic.models.vic.vic import VIC, default_vic_valgrind_suppressions_path from tonic.io import read_config, read_configobj from tonic.testing import VICTestError -from test_utils import (setup_test_dirs, print_test_dict, - replace_global_values, drop_tests, pop_run_kwargs, - check_returncode, process_error, - test_classic_driver_all_complete, - test_classic_driver_no_output_file_nans, - find_global_param_value, - check_multistream_classic) +from test_utils import ( + setup_test_dirs, print_test_dict, + replace_global_values, drop_tests, pop_run_kwargs, + check_returncode, process_error, + test_classic_driver_all_complete, + test_classic_driver_no_output_file_nans, + find_global_param_value, + check_multistream_classic, + setup_subdirs_and_fill_in_global_param_driver_match_test, + check_drivers_match_fluxes, + plot_science_tests) from test_image_driver import (test_image_driver_no_output_file_nans, - check_multistream_image) + check_multistream_image, + setup_subdirs_and_fill_in_global_param_mpi_test, + check_mpi_fluxes, check_mpi_states) from test_restart import (prepare_restart_run_periods, setup_subdirs_and_fill_in_global_param_restart_test, check_exact_restart_fluxes, @@ -32,6 +38,12 @@ test_dir = os.path.dirname(os.path.abspath(__file__)) +# Set path to valgrind supressions file if not already set. +if 'VIC_VALGRIND_SUPPRESSIONS' not in os.environ: + sup_file = os.path.join(test_dir, default_vic_valgrind_suppressions_path) + if os.path.isfile(sup_file): + os.environ["VIC_VALGRIND_SUPPRESSIONS"] = sup_file + OUTPUT_WIDTH = 100 description = ''' @@ -116,7 +128,8 @@ def main(): default=['unit', 'system'], nargs='+') parser.add_argument('--system', type=str, help='system tests configuration file', - default=os.path.join(test_dir, 'system/system_tests.cfg')) + default=os.path.join(test_dir, + 'system/system_tests.cfg')) parser.add_argument('--science', type=str, help='science tests configuration file', default=os.path.join(test_dir, 'science/science.cfg')) @@ -126,20 +139,23 @@ def main(): parser.add_argument('--release', type=str, help='release tests configuration file', default=os.path.join(test_dir, 'release/release.cfg')) - parser.add_argument('--vic_exe', type=str, - help='VIC executable to test', - default=os.path.join( - test_dir, '../vic/drivers/classic/vic_classic.exe')) - parser.add_argument('--driver', type=str, - help='VIC driver to test', - choices=['classic', 'image'], - default='classic') + parser.add_argument('--classic', type=str, + help='classic driver executable to test') + parser.add_argument('--image', type=str, + help='image driver executable to test') parser.add_argument('--output_dir', type=str, help='directory to write test output to', default='$WORKDIR/VIC_tests_{0}'.format(ymd)) parser.add_argument('--data_dir', type=str, help='directory to find test data', - default=os.path.join(test_dir, '../samples/VIC_sample_data')) + default='./samples/VIC_sample_data') + parser.add_argument('--science_test_data_dir', type=str, + help='directory to find science test data', + default='./samples/VIC_sample_data') + parser.add_argument('--nproc', type=int, + help='number of processors to use for science tests', + default=1) + args = parser.parse_args() # Define test directories @@ -147,6 +163,11 @@ def main(): out_dir = os.path.expandvars(args.output_dir) os.makedirs(out_dir, exist_ok=True) + # check to make sure science test data directory exists + science_test_data_dir = args.science_test_data_dir + if 'science' in args.tests and not os.path.exists(science_test_data_dir): + raise VICTestError("directory for science test data does not exist or has not been defined") + # Validate input directories if not (len(args.tests) == 1 and args.tests[0] == 'unit'): for d in [data_dir, test_dir]: @@ -159,9 +180,17 @@ def main(): print('Running Test Set: {0}'.format(', '.join(args.tests))) # Setup VIC executable + # --- if not only unit test --- # if not (len(args.tests) == 1 and args.tests[0] == 'unit'): - vic_exe = VIC(args.vic_exe) - print('VIC version information:\n\n{0}'.format(vic_exe.version.decode())) + dict_drivers = {} + if args.classic: + dict_drivers['classic'] = VIC(args.classic) + print('VIC classic version information:\n\n{0}'.format( + dict_drivers['classic'].version.decode())) + if args.image: + dict_drivers['image'] = VIC(args.image) + print('VIC image version information:\n\n{0}'.format( + dict_drivers['image'].version.decode())) # run test sets # unit @@ -170,21 +199,32 @@ def main(): # system if any(i in ['all', 'system'] for i in args.tests): - test_results['system'] = run_system(args.system, vic_exe, data_dir, - os.path.join(out_dir, 'system'), - args.driver) + test_results['system'] = run_system(args.system, dict_drivers, + data_dir, + os.path.join(out_dir, + 'system')) + # science if any(i in ['all', 'science'] for i in args.tests): - test_results['science'] = run_science(args.science, vic_exe, data_dir, - os.path.join(out_dir, 'science'), - args.driver) + test_results['science'] = run_science( + args.science, dict_drivers['classic'], + science_test_data_dir, + data_dir, + os.path.join(out_dir, 'science'), + 'classic', + args.nproc) # examples if any(i in ['all', 'examples'] for i in args.tests): - test_results['examples'] = run_examples(args.examples, vic_exe, - data_dir, - os.path.join( - out_dir, 'examples'), - args.driver) + if len(dict_drivers) == 1: # if only one driver + driver = list(dict_drivers.keys())[0] + vic_exe = dict_drivers[driver] + test_results['examples'] = run_examples(args.examples, vic_exe, + data_dir, + os.path.join( + out_dir, 'examples'), + driver) + else: + raise ValueError('example test only supports single driver') # release if any(i in ['all', 'release'] for i in args.tests): test_results['release'] = run_release(args.release) @@ -260,21 +300,20 @@ def run_unit_tests(test_dir): print('-'.ljust(OUTPUT_WIDTH, '-')) -def run_system(config_file, vic_exe, test_data_dir, out_dir, driver): +def run_system(config_file, dict_drivers, test_data_dir, out_dir): '''Run system tests from config file Parameters ---------- config_file : str Configuration file for system tests. - vic_exe : VIC (object) - VIC executable object (see tonic documentation). + dict_drivers : dict + Keys: driver names {'classic', 'image'} + Content: corresponding VIC executable object (see tonic documentation) test_data_dir : str Path to test data sets. out_dir : str Path to output location - driver : {'classic', 'image'} - Driver to run tests on. Returns ------- @@ -297,8 +336,16 @@ def run_system(config_file, vic_exe, test_data_dir, out_dir, driver): # Get setup config = read_configobj(config_file) - # drop invalid driver tests - config = drop_tests(config, driver) + # Process driver info + if len(dict_drivers) == 1: # if single driver + driver = list(dict_drivers.keys())[0] + vic_exe = dict_drivers[driver] + + # Drop invalid driver tests + if len(dict_drivers) == 1: # if single driver + config = drop_tests(config, driver) + else: # if multiple drivers + config = drop_tests(config, list(dict_drivers)) test_results = OrderedDict() @@ -314,39 +361,84 @@ def run_system(config_file, vic_exe, test_data_dir, out_dir, driver): mkdirs=['results', 'state', 'logs', 'plots']) # read template global parameter file - infile = os.path.join(test_dir, 'system', - test_dict['global_parameter_file']) - - with open(infile, 'r') as global_file: - global_param = global_file.read() + dict_global_param = {} + # --- if single driver --- # + if len(dict_drivers) == 1: + infile = os.path.join(test_dir, 'system', + test_dict['global_parameter_file']) + with open(infile, 'r') as global_file: + dict_global_param[driver] = global_file.read() + # --- if multiple drivers --- # + else: + for j, dr in enumerate(test_dict['driver']): + infile = os.path.join(test_dir, 'system', + test_dict['global_parameter_file'][j]) + with open(infile, 'r') as global_file: + dict_global_param[dr] = global_file.read() # If restart test, prepare running periods - # (1) Find STATESEC option (and STATE_FORMAT option for later use) - statesec = find_global_param_value(global_param, 'STATESEC') - if driver == 'classic': - state_format = find_global_param_value(global_param, - 'STATE_FORMAT') - # (2) Prepare running periods and initial state file info for restart - # test if 'exact_restart' in test_dict['check']: + if len(dict_drivers) > 1: + raise ValueError('Only support single driver for restart' + 'tests!') + global_param = dict_global_param[driver] + # (1) Find STATESEC option (and STATE_FORMAT option for later use) + statesec = find_global_param_value(global_param, 'STATESEC') + if driver == 'classic': + state_format = find_global_param_value(global_param, + 'STATE_FORMAT') + # (2) Prepare running periods and initial state file info for + # restart test run_periods = prepare_restart_run_periods( test_dict['restart'], dirs['state'], statesec) + # If mpi test, prepare a list of number of processors to be run + elif 'mpi' in test_dict['check']: + if len(dict_drivers) > 1: + raise ValueError('Only support single driver for MPI' + 'tests!') + if not isinstance(test_dict['mpi']['n_proc'], list): + raise ValueError('Need at least two values in n_proc to run' + 'mpi test!') + list_n_proc = test_dict['mpi']['n_proc'] # create template string - s = string.Template(global_param) + dict_s = {} + for dr, global_param in dict_global_param.items(): + dict_s[dr] = string.Template(global_param) # fill in global parameter options # --- if restart test, multiple runs --- # if 'exact_restart' in test_dict['check']: + s = dict_s[driver] # Set up subdirectories and fill in global parameter options # for restart testing list_global_param =\ setup_subdirs_and_fill_in_global_param_restart_test( s, run_periods, driver, dirs['results'], dirs['state'], test_data_dir) - # else, single run + # --- if mpi test, multiple runs --- # + elif 'mpi' in test_dict['check']: + s = dict_s[driver] + # Set up subdirectories and output directories in global file for + # multiprocessor testing + list_global_param = \ + setup_subdirs_and_fill_in_global_param_mpi_test( + s, list_n_proc, dirs['results'], dirs['state'], + test_data_dir) + # --- if driver-match test, one run for each driver --- # + elif 'driver_match' in test_dict['check']: + # Set up subdirectories and output directories in global file for + # driver-match testing + dict_global_param = \ + setup_subdirs_and_fill_in_global_param_driver_match_test( + dict_s, dirs['results'], dirs['state'], test_data_dir) + # --- else, single run --- # else: + if len(dict_drivers) > 1: + raise RuntimeError('Only support single driver for test' + '{}!'.format(testname)) + s = dict_s[driver] global_param = s.safe_substitute(test_data_dir=test_data_dir, result_dir=dirs['results'], state_dir=dirs['state']) @@ -357,19 +449,32 @@ def run_system(config_file, vic_exe, test_data_dir, out_dir, driver): replacements = test_dict['options'] else: replacements = OrderedDict() - # --- if STATE_FORMAT is specified, then the specified value (instead - # of the one in the global template file) --- # - if 'STATE_FORMAT' in replacements: - state_format = replacements['STATE_FORMAT'] # --- replace global options --- # + # For the purpose of exact restart, if STATE_FORMAT is specified, + # then record the specified value (instead of the one in the global + # template file) if 'exact_restart' in test_dict['check']: + if 'STATE_FORMAT' in replacements: + state_format = replacements['STATE_FORMAT'] + + if 'exact_restart' in test_dict['check'] or\ + 'mpi' in test_dict['check']: # if multiple runs for j, gp in enumerate(list_global_param): # save a copy of replacements for the next global file replacements_cp = replacements.copy() # replace global options for this global file list_global_param[j] = replace_global_values(gp, replacements) replacements = replacements_cp - else: + elif 'driver_match' in test_dict['check']: # if cross-driver runs + for dr, gp in dict_global_param.items(): + # save a copy of replacements for the next global file + replacements_cp = replacements.copy() + # replace global options for this global file + dict_global_param[dr] = replace_global_values( + gp, + replacements) + replacements = replacements_cp + else: # if single run global_param = replace_global_values(global_param, replacements) # write global parameter file @@ -386,6 +491,28 @@ def run_system(config_file, vic_exe, test_data_dir, out_dir, driver): with open(test_global_file, mode='w') as f: for line in gp: f.write(line) + elif 'mpi' in test_dict['check']: + list_test_global_file = [] + for j, gp in enumerate(list_global_param): + test_global_file = os.path.join( + dirs['test'], + '{}_globalparam_processors_{}.txt'.format( + testname, list_n_proc[j])) + list_test_global_file.append(test_global_file) + with open(test_global_file, mode='w') as f: + for line in gp: + f.write(line) + elif 'driver_match' in test_dict['check']: + dict_test_global_file = {} + for dr, gp in dict_global_param.items(): + test_global_file = os.path.join( + dirs['test'], + '{}_globalparam_{}.txt'.format( + testname, dr)) + dict_test_global_file[dr] = test_global_file + with open(test_global_file, mode='w') as f: + for line in gp: + f.write(line) else: test_global_file = os.path.join( dirs['test'], @@ -407,10 +534,45 @@ def run_system(config_file, vic_exe, test_data_dir, out_dir, driver): if 'exact_restart' in test_dict['check']: for j, test_global_file in enumerate(list_test_global_file): returncode = vic_exe.run(test_global_file, - logdir=dirs['logs']) + logdir=dirs['logs'], + **run_kwargs) # Check return code check_returncode(vic_exe, test_dict.pop('expected_retval', 0)) + elif 'mpi' in test_dict['check']: + for j, test_global_file in enumerate(list_test_global_file): + # Overwrite mpi_proc in option kwargs + n_proc = list_n_proc[j] + if n_proc == 1: + run_kwargs['mpi_proc'] = None + else: + run_kwargs['mpi_proc'] = list_n_proc[j] + # Run VIC + returncode = vic_exe.run(test_global_file, + logdir=dirs['logs'], + **run_kwargs) + # Check return code + check_returncode(vic_exe, + test_dict.pop('expected_retval', 0)) + elif 'driver_match' in test_dict['check']: + for dr in dict_test_global_file.keys(): + # Reset mpi_proc in option kwargs to None for classic + # driver run + if dr == 'classic': + run_kwargs_classic = run_kwargs + run_kwargs_classic['mpi_proc'] = None + returncode = dict_drivers[dr].run( + dict_test_global_file[dr], + logdir=dirs['logs'], + **run_kwargs_classic) + else: + returncode = dict_drivers[dr].run( + dict_test_global_file[dr], + logdir=dirs['logs'], + **run_kwargs) + # Check return code + check_returncode(dict_drivers[dr], + test_dict.pop('expected_retval', 0)) else: returncode = vic_exe.run(test_global_file, logdir=dirs['logs'], **run_kwargs) @@ -425,20 +587,31 @@ def run_system(config_file, vic_exe, test_data_dir, out_dir, driver): # Check that the simulation completed for all grid cells if 'complete' in test_dict['check']: + if len(dict_drivers) > 1: + raise RuntimeError('Only support single driver for ' + 'complete check') fnames = glob.glob(os.path.join(dirs['results'], '*')) if driver == 'classic': test_classic_driver_all_complete(fnames) + else: + raise RuntimeError('complete check only supports ' + 'classic driver') # check for nans in all example files if 'output_file_nans' in test_dict['check']: + if len(dict_drivers) > 1: + raise RuntimeError('Only support single driver for ' + 'output_file_nans check') fnames = glob.glob(os.path.join(dirs['results'], '*')) if driver == 'classic': test_classic_driver_no_output_file_nans(fnames) elif driver == 'image': - domain_file = os.path.join(test_data_dir, - test_dict['domain_file']) - test_image_driver_no_output_file_nans(fnames, - domain_file) + domain_file = os.path.join( + test_data_dir, + test_dict['domain_file']) + test_image_driver_no_output_file_nans( + fnames, + domain_file) else: raise ValueError('unknown driver') @@ -453,8 +626,14 @@ def run_system(config_file, vic_exe, test_data_dir, out_dir, driver): elif driver == 'image': check_exact_restart_states(dirs['state'], driver, run_periods, statesec) + else: + raise ValueError('unknown driver') + # check for multistream output if 'multistream' in test_dict['check']: + if len(dict_drivers) > 1: + raise ValueError('Only support single driver for ' + 'multistream check') fnames = glob.glob(os.path.join(dirs['results'], '*')) if driver == 'classic': check_multistream_classic(fnames) @@ -462,12 +641,23 @@ def run_system(config_file, vic_exe, test_data_dir, out_dir, driver): warnings.warn('Skipping multistream image driver test') # TODO: check_multistream_image(fnames) + # check for mpi multiprocessor results + if 'mpi' in test_dict['check']: + check_mpi_fluxes(dirs['results'], list_n_proc) + check_mpi_states(dirs['state'], list_n_proc) + + # check that results from different drivers match + if 'driver_match' in test_dict['check']: + check_drivers_match_fluxes(list(dict_drivers.keys()), + dirs['results']) + # if we got this far, the test passed. test_passed = True # Handle errors except Exception as e: - test_comment, error_message = process_error(e, vic_exe) + for dr, exe in dict_drivers.items(): + test_comment, error_message = process_error(e, exe) # record the test results test_results[testname] = TestResults(testname, @@ -484,7 +674,8 @@ def run_system(config_file, vic_exe, test_data_dir, out_dir, driver): return test_results -def run_science(config_file, vic_exe, test_data_dir, out_dir, driver): +def run_science(config_file, vic_exe, science_test_data_dir, + test_data_dir, out_dir, driver, nproc): '''Run science tests from config file Parameters @@ -493,12 +684,16 @@ def run_science(config_file, vic_exe, test_data_dir, out_dir, driver): Configuration file for science tests. vic_exe : VIC (object) VIC executable object (see tonic documentation). + science_test_data_dir: str + Path to science test data sets (archived VIC runs and observations) test_data_dir : str Path to test data sets. out_dir : str Path to output location driver : {'classic', 'image'} Driver to run tests on. + nproc : int + Number of processors to use for science tests Returns ------- @@ -519,7 +714,7 @@ def run_science(config_file, vic_exe, test_data_dir, out_dir, driver): print('-'.ljust(OUTPUT_WIDTH, '-')) # Get setup - config = read_config(config_file) + config = read_configobj(config_file) # drop invalid driver tests config = drop_tests(config, driver) @@ -527,18 +722,19 @@ def run_science(config_file, vic_exe, test_data_dir, out_dir, driver): test_results = OrderedDict() # Run individual tests - for i, (testname, test_dict) in enumerate(config.items()): + for i, (test_type, test_dict) in enumerate(config.items()): # print out status info print('Running test {0}/{1}: {2}'.format(i + 1, len(config.items()), - testname)) + test_type)) # Setup directories for test - dirs = setup_test_dirs(testname, out_dir, + dirs = setup_test_dirs(test_type, out_dir, mkdirs=['results', 'state', 'logs', 'plots']) # read template global parameter file - infile = os.path.join(test_dir, test_dict['global_parameter_file']) + infile = os.path.join(test_dir, 'science', + test_dict['global_parameter_file']) with open(infile, 'r') as global_file: global_param = global_file.read() @@ -547,14 +743,15 @@ def run_science(config_file, vic_exe, test_data_dir, out_dir, driver): s = string.Template(global_param) # fill in global parameter options - global_param = s.safe_substitute(test_data_dir=test_data_dir, + global_param = s.safe_substitute(test_data_dir=science_test_data_dir, + test_dir=test_dir, result_dir=dirs['results'], state_dir=dirs['state'], - testname=testname, + testname=test_type, test_root=test_dir) test_global_file = os.path.join(dirs['test'], - '{0}_globalparam.txt'.format(testname)) + '{0}_globalparam.txt'.format(test_type)) # write global parameter file with open(test_global_file, 'w') as f: @@ -573,6 +770,7 @@ def run_science(config_file, vic_exe, test_data_dir, out_dir, driver): # Run the VIC simulation returncode = vic_exe.run(test_global_file, logdir=dirs['logs'], **run_kwargs) + test_complete = True # Check return code @@ -598,6 +796,16 @@ def run_science(config_file, vic_exe, test_data_dir, out_dir, driver): else: raise ValueError('unknown driver') + # plot science test results + plot_science_tests(test_dict['driver'], + test_type, + science_test_data_dir, + dirs['results'], + dirs['plots'], + test_dict['plots'], + test_dict['compare_data'], + nproc=nproc) + # if we got this far, the test passed. test_passed = True @@ -606,12 +814,12 @@ def run_science(config_file, vic_exe, test_data_dir, out_dir, driver): test_comment, error_message = process_error(e, vic_exe) # record the test results - test_results[testname] = TestResults(testname, - test_complete=test_complete, - passed=test_passed, - comment=test_comment, - error_message=error_message, - returncode=returncode) + test_results[test_type] = TestResults(test_type, + test_complete=test_complete, + passed=test_passed, + comment=test_comment, + error_message=error_message, + returncode=returncode) print('-'.ljust(OUTPUT_WIDTH, '-')) print('Finished testing science tests.') diff --git a/tests/science/global_param.classic.4.2_ecflux.txt b/tests/science/global_param.classic.4.2_ecflux.txt new file mode 100644 index 000000000..c71981513 --- /dev/null +++ b/tests/science/global_param.classic.4.2_ecflux.txt @@ -0,0 +1,103 @@ +NLAYER 3 +NODES 10 +TIME_STEP 1 +SNOW_STEP 1 +STARTYEAR 2000 +STARTMONTH 01 +STARTDAY 01 +STARTHOUR 00 +ENDYEAR 2012 +ENDMONTH 12 +ENDDAY 31 + +FULL_ENERGY TRUE +FROZEN_SOIL FALSE + +FORCING1 $test_data_dir/inputdata/ec_flux_towers/forcing.merge.hourly/full_data_ +FORCE_FORMAT ASCII +N_TYPES 12 +FORCE_TYPE SKIP +FORCE_TYPE SKIP +FORCE_TYPE SKIP +FORCE_TYPE SKIP +FORCE_TYPE PREC UNSIGNED 40 +FORCE_TYPE AIR_TEMP SIGNED 100 +FORCE_TYPE SHORTWAVE SIGNED 100 +FORCE_TYPE LONGWAVE SIGNED 100 +FORCE_TYPE SKIP +FORCE_TYPE PRESSURE SIGNED 100 +FORCE_TYPE VP SIGNED 100 +FORCE_TYPE WIND SIGNED 100 +FORCE_DT 1 +FORCEYEAR 1980 +FORCEMONTH 01 +FORCEDAY 01 +FORCEHOUR 00 +GRID_DECIMAL 4 + +WIND_H 10.0 + +SOIL $test_data_dir/inputdata/ec_flux_towers/params/soil_param.site_test.txt +BASEFLOW ARNO +JULY_TAVG_SUPPLIED FALSE +ORGANIC_FRACT FALSE + +VEGLIB $test_data_dir/inputdata/ec_flux_towers/params/veg_lib_IGBP.fv +VEGLIB_VEGCOVER TRUE + +VEGPARAM $test_data_dir/inputdata/ec_flux_towers/params/veg_param.sites.IGBP.modis +ROOT_ZONES 3 + +VEGPARAM_LAI TRUE +VEGPARAM_VEGCOVER TRUE +VEGPARAM_ALB TRUE + +LAI_SRC FROM_VEGPARAM +VEGCOVER_SRC FROM_VEGPARAM +# ALBEDO_SRC FROM_VEGPARAM +SNOW_BAND 1 + + +RESULT_DIR $result_dir +OUT_STEP 1 +SKIPYEAR 0 +COMPRESS FALSE +BINARY_OUTPUT FALSE +ALMA_OUTPUT FALSE +MOISTFRACT FALSE +PRT_HEADER TRUE +PRT_SNOW_BAND FALSE + +N_OUTFILES 2 + +OUTFILE snow 16 +OUTVAR OUT_SWE %.6g * * +OUTVAR OUT_SNOW_DEPTH %.6g * * +OUTVAR OUT_SNOW_CANOPY %.6g * * +OUTVAR OUT_SNOW_COVER %.6g * * +OUTVAR OUT_ADVECTION %.6g * * +OUTVAR OUT_DELTACC %.6g * * +OUTVAR OUT_SNOW_FLUX %.6g * * +OUTVAR OUT_RFRZ_ENERGY %.6g * * +OUTVAR OUT_MELT_ENERGY %.6g * * +OUTVAR OUT_ADV_SENS %.6g * * +OUTVAR OUT_LATENT_SUB %.6g * * +OUTVAR OUT_SNOW_SURF_TEMP %.6g * * +OUTVAR OUT_SNOW_PACK_TEMP %.6g * * +OUTVAR OUT_SNOW_MELT %.6g * * +OUTVAR OUT_ALBEDO %.6g * * +OUTVAR OUT_SALBEDO %.6g * * + +OUTFILE en_bal 12 +OUTVAR OUT_ADV_SENS %.6g * * +OUTVAR OUT_ADVECTION %.6g * * +OUTVAR OUT_DELTAH %.6g * * +OUTVAR OUT_GRND_FLUX %.6g * * +OUTVAR OUT_IN_LONG %.6g * * +OUTVAR OUT_LATENT %.6g * * +OUTVAR OUT_LATENT_SUB %.6g * * +OUTVAR OUT_R_NET %.6g * * +OUTVAR OUT_SENSIBLE %.6g * * +OUTVAR OUT_RAD_TEMP %.6g * * +OUTVAR OUT_NET_SHORT %.6g * * +OUTVAR OUT_NET_LONG %.6g * * diff --git a/tests/science/global_param.classic.4.2_snotel.txt b/tests/science/global_param.classic.4.2_snotel.txt new file mode 100644 index 000000000..5c6dd22e9 --- /dev/null +++ b/tests/science/global_param.classic.4.2_snotel.txt @@ -0,0 +1,89 @@ +NLAYER 3 +NODES 10 +TIME_STEP 1 +SNOW_STEP 1 +STARTYEAR 2007 +STARTMONTH 11 +STARTDAY 01 +STARTHOUR 00 +ENDYEAR 2008 +ENDMONTH 07 +ENDDAY 31 + +FULL_ENERGY TRUE +FROZEN_SOIL FALSE + +QUICK_FLUX FALSE + +FORCING1 $test_data_dir/inputdata/snotel/forcings/snotel_VIC.4.1.2_forcings_ +FORCE_FORMAT ASCII +N_TYPES 7 # Number of variables (columns) +FORCE_TYPE SHORTWAVE UNSIGNED 40 +FORCE_TYPE LONGWAVE UNSIGNED 40 +FORCE_TYPE AIR_TEMP SIGNED 100 +FORCE_TYPE PRESSURE UNSIGNED 40 +FORCE_TYPE PREC UNSIGNED 40 +FORCE_TYPE VP UNSIGNED 40 +FORCE_TYPE WIND SIGNED 100 +FORCE_DT 1 +FORCEYEAR 2007 +FORCEMONTH 11 +FORCEDAY 01 +FORCEHOUR 00 +GRID_DECIMAL 4 +# WIND_H 17.5 # WIND_H is not actually used in VIC4.2 (hardcoded) + +SOIL $test_data_dir/inputdata/snotel/parameters/Snotel_VIC.4.1.2_soil.txt +BASEFLOW ARNO + +JULY_TAVG_SUPPLIED FALSE +ORGANIC_FRACT FALSE +VEGLIB $test_data_dir/inputdata/snotel/parameters/Snotel_VIC.4.1.2_veglib.txt +VEGPARAM $test_data_dir/inputdata/snotel/parameters/Snotel_VIC.4.1.2_vegparam.txt +ROOT_ZONES 3 + +VEGPARAM_LAI TRUE +LAI_SRC FROM_VEGPARAM + +SNOW_BAND 1 + +RESULT_DIR $result_dir +OUT_STEP 24 +SKIPYEAR 0 +COMPRESS FALSE +BINARY_OUTPUT FALSE +ALMA_OUTPUT FALSE +MOISTFRACT FALSE +PRT_HEADER TRUE +PRT_SNOW_BAND FALSE + + +N_OUTFILES 1 + +OUTFILE outfile 26 +OUTVAR OUT_SWE %.6g * * +OUTVAR OUT_SNOW_DEPTH %.6g * * +OUTVAR OUT_SNOW_CANOPY %.6g * * +OUTVAR OUT_SNOW_COVER %.6g * * +OUTVAR OUT_ADVECTION %.6g * * +OUTVAR OUT_DELTACC %.6g * * +OUTVAR OUT_SNOW_FLUX %.6g * * +OUTVAR OUT_RFRZ_ENERGY %.6g * * +OUTVAR OUT_MELT_ENERGY %.6g * * +OUTVAR OUT_ADV_SENS %.6g * * +OUTVAR OUT_LATENT_SUB %.6g * * +OUTVAR OUT_SNOW_SURF_TEMP %.6g * * +OUTVAR OUT_SNOW_PACK_TEMP %.6g * * +OUTVAR OUT_SNOW_MELT %.6g * * +OUTVAR OUT_ALBEDO %.6g * * +OUTVAR OUT_SALBEDO %.6g * * +OUTVAR OUT_ADV_SENS %.6g * * +OUTVAR OUT_ADVECTION %.6g * * +OUTVAR OUT_DELTAH %.6g * * +OUTVAR OUT_GRND_FLUX %.6g * * +OUTVAR OUT_IN_LONG %.6g * * +OUTVAR OUT_LATENT %.6g * * +OUTVAR OUT_LATENT_SUB %.6g * * +OUTVAR OUT_R_NET %.6g * * +OUTVAR OUT_SENSIBLE %.6g * * +OUTVAR OUT_RAD_TEMP %.6g * * diff --git a/tests/science/global_param.classic.5.0_ecflux.txt b/tests/science/global_param.classic.5.0_ecflux.txt new file mode 100644 index 000000000..7f6b43410 --- /dev/null +++ b/tests/science/global_param.classic.5.0_ecflux.txt @@ -0,0 +1,102 @@ +NLAYER 3 +NODES 10 +MODEL_STEPS_PER_DAY 24 +SNOW_STEPS_PER_DAY 24 +RUNOFF_STEPS_PER_DAY 24 +STARTYEAR 2000 +STARTMONTH 01 +STARTDAY 01 +STARTSEC 00 +ENDYEAR 2012 +ENDMONTH 12 +ENDDAY 31 + +FULL_ENERGY TRUE +FROZEN_SOIL FALSE + +QUICK_FLUX FALSE + +FORCING1 $test_data_dir/inputdata/ec_flux_towers/forcing.merge.hourly/full_data_ +FORCE_FORMAT ASCII +FORCE_TYPE SKIP +FORCE_TYPE SKIP +FORCE_TYPE SKIP +FORCE_TYPE SKIP +FORCE_TYPE PREC UNSIGNED 40 +FORCE_TYPE AIR_TEMP SIGNED 100 +FORCE_TYPE SWDOWN SIGNED 100 +FORCE_TYPE LWDOWN SIGNED 100 +FORCE_TYPE SKIP +FORCE_TYPE PRESSURE SIGNED 100 +FORCE_TYPE VP SIGNED 100 +FORCE_TYPE WIND SIGNED 100 +FORCE_STEPS_PER_DAY 24 +FORCEYEAR 1980 +FORCEMONTH 01 +FORCEDAY 01 +GRID_DECIMAL 4 + +WIND_H 10.0 + +SOIL $test_data_dir/inputdata/ec_flux_towers/params/soil_param.site_test.txt +BASEFLOW ARNO +JULY_TAVG_SUPPLIED FALSE +ORGANIC_FRACT FALSE + +VEGLIB $test_data_dir/inputdata/ec_flux_towers/params/veg_lib_IGBP.fv +VEGLIB_FCAN TRUE + +VEGPARAM $test_data_dir/inputdata/ec_flux_towers/params/veg_param.sites.IGBP.modis +ROOT_ZONES 3 + +VEGPARAM_LAI TRUE +VEGPARAM_FCAN TRUE +VEGPARAM_ALB TRUE + +LAI_SRC FROM_VEGPARAM +FCAN_SRC FROM_VEGPARAM +# ALBEDO_SRC FROM_VEGPARAM + +SNOW_BAND 1 + +RESULT_DIR $result_dir + +CONSTANTS $test_dir/science/vic_parameters_42_compat.txt + +OUTFILE snow 16 +AGGFREQ NHOURS +COMPRESS FALSE +OUT_FORMAT ASCII +OUTVAR OUT_SWE %.6g * * +OUTVAR OUT_SNOW_DEPTH %.6g * * +OUTVAR OUT_SNOW_CANOPY %.6g * * +OUTVAR OUT_SNOW_COVER %.6g * * +OUTVAR OUT_ADVECTION %.6g * * +OUTVAR OUT_DELTACC %.6g * * +OUTVAR OUT_SNOW_FLUX %.6g * * +OUTVAR OUT_RFRZ_ENERGY %.6g * * +OUTVAR OUT_MELT_ENERGY %.6g * * +OUTVAR OUT_ADV_SENS %.6g * * +OUTVAR OUT_LATENT_SUB %.6g * * +OUTVAR OUT_SNOW_SURF_TEMP %.6g * * +OUTVAR OUT_SNOW_PACK_TEMP %.6g * * +OUTVAR OUT_SNOW_MELT %.6g * * +OUTVAR OUT_ALBEDO %.6g * * +OUTVAR OUT_SALBEDO %.6g * * + +OUTFILE en_bal 12 +AGGFREQ NHOURS +COMPRESS FALSE +OUT_FORMAT ASCII +OUTVAR OUT_ADV_SENS %.6g * * +OUTVAR OUT_ADVECTION %.6g * * +OUTVAR OUT_DELTAH %.6g * * +OUTVAR OUT_GRND_FLUX %.6g * * +OUTVAR OUT_IN_LONG %.6g * * +OUTVAR OUT_LATENT %.6g * * +OUTVAR OUT_LATENT_SUB %.6g * * +OUTVAR OUT_R_NET %.6g * * +OUTVAR OUT_SENSIBLE %.6g * * +OUTVAR OUT_RAD_TEMP %.6g * * +OUTVAR OUT_SWNET %.6g * * +OUTVAR OUT_LWNET %.6g * * diff --git a/tests/science/global_param.classic.5.0_snotel.txt b/tests/science/global_param.classic.5.0_snotel.txt new file mode 100644 index 000000000..8622ef609 --- /dev/null +++ b/tests/science/global_param.classic.5.0_snotel.txt @@ -0,0 +1,84 @@ +NLAYER 3 +NODES 10 +MODEL_STEPS_PER_DAY 24 +SNOW_STEPS_PER_DAY 24 +RUNOFF_STEPS_PER_DAY 24 +STARTYEAR 2007 +STARTMONTH 11 +STARTDAY 01 +STARTSEC 00 +ENDYEAR 2008 +ENDMONTH 07 +ENDDAY 31 + +FULL_ENERGY TRUE +FROZEN_SOIL FALSE + +QUICK_FLUX FALSE + +FORCING1 $test_data_dir/inputdata/snotel/forcings/snotel_VIC.4.1.2_forcings_ +FORCE_FORMAT ASCII +FORCE_TYPE SWDOWN +FORCE_TYPE LWDOWN +FORCE_TYPE AIR_TEMP +FORCE_TYPE PRESSURE +FORCE_TYPE PREC +FORCE_TYPE VP +FORCE_TYPE WIND + +FORCE_STEPS_PER_DAY 24 +FORCEYEAR 2007 +FORCEMONTH 11 +FORCEDAY 01 +GRID_DECIMAL 4 +WIND_H 10.0 + +SOIL $test_data_dir/inputdata/snotel/parameters/Snotel_VIC.4.1.2_soil.txt +BASEFLOW ARNO + +JULY_TAVG_SUPPLIED FALSE +ORGANIC_FRACT FALSE + +VEGLIB $test_data_dir/inputdata/snotel/parameters/Snotel_VIC.4.1.2_veglib.txt +VEGPARAM $test_data_dir/inputdata/snotel/parameters/Snotel_VIC.4.1.2_vegparam.txt +ROOT_ZONES 3 + +VEGPARAM_LAI TRUE +LAI_SRC FROM_VEGPARAM + +SNOW_BAND 1 + +RESULT_DIR $result_dir + +CONSTANTS $test_dir/science/vic_parameters_42_compat.txt + +OUTFILE outfile 24 +AGGFREQ NDAYS +COMPRESS FALSE +OUT_FORMAT ASCII +OUTVAR OUT_SWE %.6g * * +OUTVAR OUT_SNOW_DEPTH %.6g * * +OUTVAR OUT_SNOW_CANOPY %.6g * * +OUTVAR OUT_SNOW_COVER %.6g * * +OUTVAR OUT_ADVECTION %.6g * * +OUTVAR OUT_DELTACC %.6g * * +OUTVAR OUT_SNOW_FLUX %.6g * * +OUTVAR OUT_RFRZ_ENERGY %.6g * * +OUTVAR OUT_MELT_ENERGY %.6g * * +OUTVAR OUT_ADV_SENS %.6g * * +OUTVAR OUT_LATENT_SUB %.6g * * +OUTVAR OUT_SNOW_SURF_TEMP %.6g * * +OUTVAR OUT_SNOW_PACK_TEMP %.6g * * +OUTVAR OUT_SNOW_MELT %.6g * * +OUTVAR OUT_ALBEDO %.6g * * +OUTVAR OUT_SALBEDO %.6g * * +OUTVAR OUT_ADV_SENS %.6g * * +OUTVAR OUT_ADVECTION %.6g * * +OUTVAR OUT_DELTAH %.6g * * +OUTVAR OUT_GRND_FLUX %.6g * * +OUTVAR OUT_IN_LONG %.6g * * +OUTVAR OUT_LATENT %.6g * * +OUTVAR OUT_LATENT_SUB %.6g * * +OUTVAR OUT_R_NET %.6g * * +OUTVAR OUT_SENSIBLE %.6g * * +OUTVAR OUT_RAD_TEMP %.6g * * diff --git a/tests/science/science.cfg b/tests/science/science.cfg index b64aea458..499c140f1 100644 --- a/tests/science/science.cfg +++ b/tests/science/science.cfg @@ -1 +1,65 @@ -[test] +[science_test_snotel] +driver = classic +test_description = snotel point simulations +global_parameter_file = global_param.classic.5.0_snotel.txt +expected_retval = 0 +check = output_file_nans +plots = water_year +[[compare_data]] +[[[VIC.4.2.d]]] +version = 4.2 +archive = vic_4_2 +color = 'b' +linestyle = '-' +linewidth = 4.0 +zorder = 5 +compare_to = snotel +[[[VIC.5.0.0.rc1]]] +version = 5.0 +archive = vic_5_0 +color = 'r' +linestyle = '-' +linewidth = 2.0 +zorder = 10 +compare_to = snotel +[[[snotel]]] +version = None +archive = snotel +color = 'k' +linestyle = '--' +linewidth = 4.0 +zorder = 1 +compare_to = snotel + +[science_test_fluxnet] +driver = classic +test_description = Ameriflux point simulations +global_parameter_file = global_param.classic.5.0_ecflux.txt +expected_retval = 0 +check = output_file_nans +plots = annual_mean_diurnal_cycle, monthly_mean_diurnal_cycle +[[compare_data]] +[[[VIC.4.2.d]]] +version = 4.2 +archive = vic_4_2 +color = 'b' +linestyle = '-' +linewidth = 4.0 +zorder = 5 +compare_to = ecflux +[[[VIC.5.0.0.rc1]]] +version = 5.0 +archive = vic_5_0 +color = 'r' +linestyle = '-' +linewidth = 2.0 +zorder = 10 +compare_to = ecflux +[[[ecflux]]] +version = None +archive = ec_flux_towers +color = 'k' +linestyle = '--' +linewidth = 4.0 +zorder = 1 +compare_to = None diff --git a/tests/science/vic_parameters_42_compat.txt b/tests/science/vic_parameters_42_compat.txt new file mode 100644 index 000000000..fe94a7af0 --- /dev/null +++ b/tests/science/vic_parameters_42_compat.txt @@ -0,0 +1,279 @@ + # @section DESCRIPTION + # + # VIC Model Parameters Namelist + # + # This file includes all physical constants from VIC 4.2 for compatible + # testing between VIC 4.2 and VIC 5.0. + # + # @section LICENSE + # + # The Variable Infiltration Capacity (VIC) macroscale hydrological model + # Copyright (C) 2016 The Computational Hydrology Group, Department of Civil + # and Environmental Engineering, University of Washington. + # + # The VIC model 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 2 + # 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, write to the Free Software Foundation, Inc., + # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + ############################################################################## + +############################################################################### +LAPSE_RATE -0.0065 # temperature lapse rate in C/m +############################################################################### + +############################################################################### +ALBEDO_BARE_SOIL 0.2 # albedo for bare soil + +EMISS_GRND 1.0 # Ground emissivity +EMISS_VEG 1.0 # Veg emissivity +EMISS_ICE 0.97 # Ice emissivity +EMISS_SNOW 1.0 # Snow emissivity +############################################################################### + + +DEFAULT_WIND_SPEED 3.0 # Default wind speed [m/s] used when wind is not + # supplied as a forcing + +############################################################################### +# Physical Constraints +SOIL_RESID_MOIST 0.0 # define residual moisture content of soil column +SOIL_SLAB_MOIST_FRACT 1.0 # Volumetric moisture content (fraction of porosity) + # in the soil/rock below the bottom soil layer; this + # assumes that the soil below the bottom layer has + # the same texture as the bottom layer. + +# water holding capacity of snow as a fraction of snow-water-equivalent +VEG_LIQUID_WATER_CAPACITY 0.035 + +# multiplier to calculate the amount of available snow interception as a +#function of LAI (m) +VEG_LAI_SNOW_MULTIPLIER 0.0005 + +# the amount of snow on the canopy that can only be melted off. (m) +VEG_MIN_INTERCEPTION_STORAGE 0.005 + +# Coefficient multiplied by the LAI to determine the amount of water that can +# be stored in the canopy +VEG_LAI_WATER_FACTOR 0.1 +############################################################################### + +############################################################################### +# Canopy Resistance Parameters +CANOPY_CLOSURE 4000.0 # Threshold vapor pressure deficit for stomatal closure (Pa) +CANOPY_RSMAX 5000.0 # Maximum allowable resistance (s/m) +CANOPY_VPDMINFACTOR 0.1 # Minimum allowable vapor pressure deficit factor +############################################################################### + +############################################################################### +# Lake Model Parameters + +# lake model related constants +LAKE_TMELT 0.0 +LAKE_MAX_SURFACE_LAKE 0.6 # max. surface layer thickness for E-B (m) +LAKE_BETA 0.001 # Curve shape parameter for lake profile. +LAKE_FRACMIN 0.10 # min ice thickness in meters +LAKE_FRACLIM 0.02 # lower limit on fractional ice cover +LAKE_DM 1.38889E-07 # molecular diffusivity of water +LAKE_SNOWCRIT 0.05 # for albedo, in m +LAKE_ZWATER 0.0045 # 0.004 - original value +LAKE_ZSNOW 0.005 +LAKE_RHOSNOW 250.0 # densities snow on ice +LAKE_CONDI 2.3 # thermal conductivity of ice +LAKE_CONDS 0.7 # thermal conductivity of snow + +# attenuation of short and longwave radiation through ice (1/m) +LAKE_LAMISW 1.5 # 1.5 in Patterson & Hamblin +LAKE_LAMILW 20.0 # 20.0 in Patterson & Hamblin + +# attenuation of short and longwave radiation through snow (1/m) +LAKE_LAMSSW 6.0 # 6.0 in Patterson & Hamblin +LAKE_LAMSLW 20.0 # 20.0 in Patterson & Hamblin + +# attenuation of short and longwave radiation through water (1/m) +LAKE_LAMWSW 0.3 # San Fran Bay data: 0.31 - 29.9 1/m (visible) +LAKE_LAMWLW 1.4 # Hostetler and Bartlein assume 0.85 1/m (total) +LAKE_A1 0.7 # Fraction of radiation in visible band. +LAKE_A2 0.3 # Fraction of radiation in infrared band. +LAKE_QWTAU 43200.0 # D. Pollard sub-ice time constant. (86400. / 2.) +############################################################################### + +############################################################################### +# Saturated Vapor Pressure Curve Parameters +SVP_A 0.61078 +SVP_B 17.269 +SVP_C 237.3 +############################################################################### + +############################################################################### +# Carbon Cycling constants +CARBON_CATMCURRENT 383.0 # Current global atmospheric CO2 mixing ratio (ppm) +CARBON_SW2PAR 0.45 # Empirical ratio of PAR [W/m2] to SHORTWAVE [W/m2] from Lopez et al., 2001 +############################################################################### + +############################################################################### +# Photosynthesis Parameters +PHOTO_OMEGA 0.12 # single leaf scattering albedo +PHOTO_LAIMAX 8.0 # Maximum LAI in nitrogen scaling +PHOTO_LAILIMIT 3.0 # Minimum LAI in nitrogen scaling and maximum LAI in PAR + # computation +PHOTO_LAIMIN 1.0e-9 # Minimum LAI in PAR computation +PHOTO_EPAR 2.2e5 # Energy content of PAR [J/mol photons] + # = (4.6 mol/MJ PAR)^-1 +PHOTO_FCMAX 0.9 # Maximum fractional veg cover + # (1-FcMax) = min amount of ground visible +PHOTO_FCMIN 1.0e-3 # Minimum fractional veg cover + # (1-FcMin) = max amount of ground visible +PHOTO_ZENITHMIN 0.0174524 # Check for solar zenith angle > 89 deg +PHOTO_ZENITHMINPAR 1.0e-3 # Cosine of the minimum solar zenith angle for + # photosynthesis to take place +PHOTO_ALBSOIPARMIN 0.0 # Minimum soil reflectivity in PAR range +PHOTO_MINMAXETRANS 1e-12 # Minimum of maximum electron transport rate + # [10e-12 mol/(m^2 s)] +PHOTO_MINSTOMCOND 0.0 # Minimum stomatal conductance [mol H2O/m2s] + +# Factors that relate leaf internal CO2 concentration to ambient CO2 +# concentration +PHOTO_FCI1C3 0.87 # C3 Plants +PHOTO_FCI1C4 0.67 # C4 Plants + +# C3 PLANTS: FARQUHAR, G.D., S. VON CAEMMERER AND J.A. BERRY, 1980. A +# BIOCHEMICAL MODEL OF PHOTOYNTHESIS IN LEAVES OF C3 SPECIES. PLANTA 149, +# 78-90. +PHOTO_OX 0.21 # OXYGEN CONCENTRATION [MOL(O2) / MOL(AIR)] +PHOTO_KC0 460.0e-6 # MICHAELIS-MENTEN CONSTANT FOR CO2 AT 25C + # [MOL(CO2) / MOL(AIR)] +PHOTO_KO0 330.0e-3 # MICHAELIS-MENTEN CONSTANT FOR O2 AT 25C + # [MOL(O2) / MOL(AIR)] +PHOTO_EC 59356.0 # ACTIVATION ENERGY FOR KC [J / MOL] +PHOTO_EO 35948.0 # ACTIVATION ENERGY FOR KO [J / MOL] +PHOTO_EV 58520.0 # ACTIVATION ENERGY FOR VCMAX [J / MOL] +PHOTO_ER 45000.0 # ACTIVATION ENERGY FOR DARK RESPIRATION [J / MOL] +PHOTO_ALC3 0.28 # EFFICIENCY OF OF PHOTON CAPTURE +PHOTO_FRDC3 0.011 # RATIO OF DARK RESPIRATION TO "PVM" AT 25C for C3 + +# C4 PLANTS: COLLATZ, G.J., M. RIBAS-CARBO AND J.A. BERRY, 1992. COUPLED +# PHOTOSYNTHESIS-STOMATAL CONDUCTANCE MODEL FOR LEAVES OF C4 PLANTS. AUST. J. +# PLANT PHYSIOL. 19, 519-538. +PHOTO_EK 50967.0 # Q10=2 (Collatz et al. 1992) +PHOTO_ALC4 0.04 # EFFECTIVE QUANTUM EFFICIENCY +PHOTO_FRDC4 0.042 # RATIO OF DARK RESPIRATION TO "PVM" AT 25C for C4 +PHOTO_THETA 0.83 # CURVATURE PARAMETER + +# Plant Maintenance and Growth Respiration Parameters +PHOTO_FRLEAF 0.4 # Ratio of canopy leaf respiration to whole plant + # maintenance respiration +PHOTO_FRGROWTH 0.25 # Ratio of plant growth respiration to NPP +############################################################################### + +############################################################################### +# Soil Respiration Parameters +SRESP_E0_LT 308.56 # Lloyd-Taylor E0 parameter [K] +SRESP_T0_LT 227.13 # Lloyd-Taylor T0 parameter [K] +SRESP_WMINFM 0.0 # minimum soil moisture (fraction) at which soil + # respiration can occur +SRESP_WMAXFM 1.0 # maximum soil moisture (fraction) at which soil + # respiration can occur +SRESP_WOPTFM 0.5 # soil moisture (fraction) at which maximum soil + # respiration occurs +SRESP_RHSAT 0.15 # ratio of soil respiration rate under saturated conditions + # (w=wmaxFM) to that under optimal conditions (w=woptFM) +SRESP_RFACTOR 0.5 # scaling factor to account for other (non-moisture) + # sources of inhibition of respiration +SRESP_TAULITTER 2.86 # Litter pool turnover time [y] +SRESP_TAUINTER 33.3 # Intermediate pool turnover time [y] +SRESP_TAUSLOW 1000.0 # Slow pool turnover time [y] +SRESP_FAIR 0.7 # Fraction of respired carbon from litter pool that is lost + # to atmosphere +SRESP_FINTER 0.985 # Fraction of [respired carbon from litter pool that goes + # to soil] that goes to intermediate pool +############################################################################### + +############################################################################### +# Iteration bracket widths +SNOW_DT 5.0 # Used to bracket snow surface temperatures while computing the + # snow surface energy balance (C) +SURF_DT 1.0 # Used to bracket soil surface temperatures while computing + # energy balance (C) +SOIL_DT 0.25 # Used to bracket soil temperatures while solving the soil + # thermal flux (C) +CANOPY_DT 1.0 # Used to bracket canopy air temperatures while computing + #energy balance (C) +CANOPY_VP 25.0 # Used to bracket canopy vapor pressures while computing + # moisture balance (Pa) +############################################################################### + +############################################################################### +# Snow Parameters + +# maximum depth of the surface layer in water equivalent (m) [default 0.125] +SNOW_MAX_SURFACE_SWE 0.125 + +# density of new fallen snow [50] +SNOW_NEW_SNOW_DENSITY 50.0 + +# Density limit used in calculation of destructive metamorphism +SNOW_DENS_DMLIMIT 100.0 # (kg/m^3) + +SNOW_DENS_MAX_CHANGE 0.9 + +# Constants in snow density computation +SNOW_DENS_ETA0 3.6e6 # viscosity of snow at T=0C and density = 0 + # used in calculation of true viscosity (Ns/m2) +SNOW_DENS_C1 0.04 +SNOW_DENS_C2 2.778e-6 +SNOW_DENS_C5 0.08 # constant used in snow viscosity calculation, taken from + # SNTHRM.89 (/C) +SNOW_DENS_C6 0.021 # constant used in snow viscosity calculation, taken from + # SNTHRM.89 (kg/m3) +SNOW_DENS_F 0.6 # internal compaction rate coefficient + +# Minimum SWQ for which the snowpack energy balance is computed independent of +# the soil surface temperature +SNOW_MIN_SWQ_EB_THRES 0.0010 + +# Attenuation coefficients for shortwave in a snowpack. Values and equation +# taken from Patterson and Hamblin, 1988 +SNOW_A1 0.7 +SNOW_A2 0.3 +SNOW_L1 6.0 # (1/m) +SNOW_L2 20.0 # (1/m) + +# Snow albedo curve parameters. Defaults are from Bras p263. Should not be +# changed except for serious problems with snow melt +SNOW_NEW_SNOW_ALB 0.85 +SNOW_ALB_ACCUM_A 0.94 +SNOW_ALB_ACCUM_B 0.58 +SNOW_ALB_THAW_A 0.82 +SNOW_ALB_THAW_B 0.46 + +# Defines the minimum amount of new snow (mm) which will reset the snowpack +# albedo to new snow +SNOW_TRACESNOW 0.03 +############################################################################### + +############################################################################### +# Blowing Snow Parameters +BLOWING_KA 0.0245187 # thermal conductivity of air (W/mK) +BLOWING_CSALT 0.68 # saltation constant m/s +BLOWING_UTHRESH 0.25 # threshold shear velocity m/s +BLOWING_KIN_VIS 1.3e-5 # Kinemativ viscosity of air (m2/s) +BLOWING_MAX_ITER 100 # Max. iterations for numerical integration +BLOWING_K 5 +BLOWING_MACHEPS 1.0e-6 # Accuracy tolerance for numerical integration +BLOWING_SETTLING 0.3 # Particle settling velocity m/s +BLOWING_NUMINCS 10 # Number of prob intervals to solve for wind. +############################################################################### + +############################################################################### +# July average temperature for tree line +TREELINE_TEMPERATURE 10.0 +############################################################################### diff --git a/tests/system/global.classic.STEHE.allhistvars.txt b/tests/system/global.classic.STEHE.allhistvars.txt index 77aa0f6c6..92dcc59ea 100644 --- a/tests/system/global.classic.STEHE.allhistvars.txt +++ b/tests/system/global.classic.STEHE.allhistvars.txt @@ -216,3 +216,5 @@ OUTVAR OUT_SNOW_MELT_BAND OUTVAR OUT_SNOW_PACKT_BAND OUTVAR OUT_SNOW_SURFT_BAND OUTVAR OUT_SWE_BAND +OUTVAR OUT_TIME_VICRUN_WALL +OUTVAR OUT_TIME_VICRUN_CPU diff --git a/tests/system/global.image.STEHE.allhistvars.txt b/tests/system/global.image.STEHE.allhistvars.txt index e028f616c..4b29b7afc 100644 --- a/tests/system/global.image.STEHE.allhistvars.txt +++ b/tests/system/global.image.STEHE.allhistvars.txt @@ -34,12 +34,9 @@ FORCE_TYPE VP shum FORCE_TYPE WIND wind WIND_H 10.0 -SOIL $test_data_dir/image/Stehekin/parameters/Stehekin_test_params_20160327.nc +PARAMETERS $test_data_dir/image/Stehekin/parameters/Stehekin_test_params_20160327.nc NODES 3 -VEGPARAM $test_data_dir/image/Stehekin/parameters/Stehekin_test_params_20160327.nc -ROOT_ZONES 3 -VEGLIB $test_data_dir/image/Stehekin/parameters/Stehekin_test_params_20160327.nc -SNOW_BAND 1 $test_data_dir/image/Stehekin/parameters/Stehekin_test_params_20160327.nc +SNOW_BAND TRUE RESULT_DIR $result_dir/ @@ -207,3 +204,5 @@ OUTVAR OUT_SNOW_MELT_BAND OUTVAR OUT_SNOW_PACKT_BAND OUTVAR OUT_SNOW_SURFT_BAND OUTVAR OUT_SWE_BAND +OUTVAR OUT_TIME_VICRUN_WALL +OUTVAR OUT_TIME_VICRUN_CPU diff --git a/tests/system/global.image.STEHE.mpi.txt b/tests/system/global.image.STEHE.mpi.txt new file mode 100644 index 000000000..f247f6d16 --- /dev/null +++ b/tests/system/global.image.STEHE.mpi.txt @@ -0,0 +1,71 @@ +NODES 3 +MODEL_STEPS_PER_DAY 24 +SNOW_STEPS_PER_DAY 24 +RUNOFF_STEPS_PER_DAY 24 +STARTYEAR 1949 +STARTMONTH 1 +STARTDAY 1 +ENDYEAR 1949 +ENDMONTH 1 +ENDDAY 10 +CALENDAR PROLEPTIC_GREGORIAN +FULL_ENERGY FALSE +FROZEN_SOIL FALSE + +DOMAIN $test_data_dir/image/Stehekin/parameters/domain.stehekin.20151028.nc +DOMAIN_TYPE LAT lat +DOMAIN_TYPE LON lon +DOMAIN_TYPE MASK mask +DOMAIN_TYPE AREA area +DOMAIN_TYPE FRAC frac +DOMAIN_TYPE YDIM lat +DOMAIN_TYPE XDIM lon + +#INIT_STATE +STATENAME $state_dir/states +STATEYEAR 1949 +STATEMONTH 1 +STATEDAY 10 +STATESEC 82800 + +FORCING1 $test_data_dir/image/Stehekin/forcings/Stehekin_image_test.forcings_10days. +FORCE_TYPE AIR_TEMP tas +FORCE_TYPE PREC prcp +FORCE_TYPE PRESSURE pres +FORCE_TYPE SWDOWN dswrf +FORCE_TYPE LWDOWN dlwrf +FORCE_TYPE VP shum +FORCE_TYPE WIND wind +WIND_H 10.0 + +PARAMETERS $test_data_dir/image/Stehekin/parameters/Stehekin_test_params_20160327.nc +BASEFLOW ARNO +JULY_TAVG_SUPPLIED FALSE +ORGANIC_FRACT FALSE +LAI_SRC FROM_VEGPARAM +SNOW_BAND TRUE + +RESULT_DIR $result_dir + +OUTFILE fluxes +AGGFREQ NHOURS 1 +OUTVAR OUT_PREC +OUTVAR OUT_RAINF +OUTVAR OUT_SNOWF +OUTVAR OUT_AIR_TEMP +OUTVAR OUT_SWDOWN +OUTVAR OUT_LWDOWN +OUTVAR OUT_PRESSURE +OUTVAR OUT_WIND +OUTVAR OUT_DENSITY +OUTVAR OUT_REL_HUMID +OUTVAR OUT_QAIR +OUTVAR OUT_VP +OUTVAR OUT_VPD +OUTVAR OUT_RUNOFF +OUTVAR OUT_BASEFLOW +OUTVAR OUT_EVAP +OUTVAR OUT_SWE +OUTVAR OUT_SOIL_MOIST +OUTVAR OUT_ALBEDO +OUTVAR OUT_SOIL_TEMP diff --git a/tests/system/global.image.STEHE.multistream.txt b/tests/system/global.image.STEHE.multistream.txt index 416b8faa8..0496f8d96 100644 --- a/tests/system/global.image.STEHE.multistream.txt +++ b/tests/system/global.image.STEHE.multistream.txt @@ -34,12 +34,8 @@ FORCE_TYPE VP shum FORCE_TYPE WIND wind WIND_H 10.0 -SOIL $test_data_dir/image/Stehekin/parameters/Stehekin_test_params_20160327.nc +PARAMETERS $test_data_dir/image/Stehekin/parameters/Stehekin_test_params_20160327.nc NODES 3 -VEGPARAM $test_data_dir/image/Stehekin/parameters/Stehekin_test_params_20160327.nc -ROOT_ZONES 3 -VEGLIB $test_data_dir/image/Stehekin/parameters/Stehekin_test_params_20160327.nc -SNOW_BAND 1 $test_data_dir/image/Stehekin/parameters/Stehekin_test_params_20160327.nc RESULT_DIR $result_dir/ diff --git a/tests/system/global.image.STEHE.restart.FROZEN_SOIL.txt b/tests/system/global.image.STEHE.restart.FROZEN_SOIL.txt index 57849869b..97a465d61 100644 --- a/tests/system/global.image.STEHE.restart.FROZEN_SOIL.txt +++ b/tests/system/global.image.STEHE.restart.FROZEN_SOIL.txt @@ -38,20 +38,16 @@ FORCE_TYPE VP shum FORCE_TYPE WIND wind WIND_H 10.0 -SOIL $test_data_dir/image/Stehekin/parameters/Stehekin_test_params_20160327.FROZEN_SOIL.nc +PARAMETERS $test_data_dir/image/Stehekin/parameters/Stehekin_test_params_20160327.FROZEN_SOIL.nc BASEFLOW ARNO JULY_TAVG_SUPPLIED FALSE ORGANIC_FRACT FALSE -VEGLIB $test_data_dir/image/Stehekin/parameters/Stehekin_test_params_20160327.nc -VEGPARAM $test_data_dir/image/Stehekin/parameters/Stehekin_test_params_20160327.nc -ROOT_ZONES 3 LAI_SRC FROM_VEGPARAM -SNOW_BAND 1 $test_data_dir/image/Stehekin/parameters/Stehekin_test_params_20160327.nc RESULT_DIR $result_dir OUTFILE fluxes -AGGFREQ NHOURS 1 +AGGFREQ NHOURS 1 OUTVAR OUT_PREC OUTVAR OUT_RAINF OUTVAR OUT_SNOWF diff --git a/tests/system/global.image.STEHE.restart.txt b/tests/system/global.image.STEHE.restart.txt index 761f5ca98..4b26cb067 100644 --- a/tests/system/global.image.STEHE.restart.txt +++ b/tests/system/global.image.STEHE.restart.txt @@ -38,15 +38,12 @@ FORCE_TYPE VP shum FORCE_TYPE WIND wind WIND_H 10.0 -SOIL $test_data_dir/image/Stehekin/parameters/Stehekin_test_params_20160327.nc +PARAMETERS $test_data_dir/image/Stehekin/parameters/Stehekin_test_params_20160327.nc BASEFLOW ARNO JULY_TAVG_SUPPLIED FALSE ORGANIC_FRACT FALSE -VEGLIB $test_data_dir/image/Stehekin/parameters/Stehekin_test_params_20160327.nc -VEGPARAM $test_data_dir/image/Stehekin/parameters/Stehekin_test_params_20160327.nc -ROOT_ZONES 3 LAI_SRC FROM_VEGPARAM -SNOW_BAND 1 $test_data_dir/image/Stehekin/parameters/Stehekin_test_params_20160327.nc +SNOW_BAND TRUE RESULT_DIR $result_dir diff --git a/tests/system/global.image.STEHE.txt b/tests/system/global.image.STEHE.txt index 8ef5326cf..b50e1978b 100644 --- a/tests/system/global.image.STEHE.txt +++ b/tests/system/global.image.STEHE.txt @@ -13,7 +13,12 @@ CALENDAR PROLEPTIC_GREGORIAN FULL_ENERGY TRUE FROZEN_SOIL FALSE -AERO_RESIST_CANSNOW AR_406 +#INIT_STATE +STATENAME $state_dir/states +STATEYEAR 1949 +STATEMONTH 1 +STATEDAY 10 +STATESEC 82800 DOMAIN $test_data_dir/image/Stehekin/parameters/domain.stehekin.20151028.nc DOMAIN_TYPE LAT lat @@ -34,12 +39,12 @@ FORCE_TYPE VP shum FORCE_TYPE WIND wind WIND_H 10.0 -SOIL $test_data_dir/image/Stehekin/parameters/Stehekin_test_params_20160327.nc +PARAMETERS $test_data_dir/image/Stehekin/parameters/Stehekin_test_params_20160327.nc +SNOW_BAND TRUE NODES 3 -VEGPARAM $test_data_dir/image/Stehekin/parameters/Stehekin_test_params_20160327.nc -ROOT_ZONES 3 -VEGLIB $test_data_dir/image/Stehekin/parameters/Stehekin_test_params_20160327.nc -SNOW_BAND 1 $test_data_dir/image/Stehekin/parameters/Stehekin_test_params_20160327.nc +BASEFLOW ARNO +JULY_TAVG_SUPPLIED FALSE +ORGANIC_FRACT FALSE RESULT_DIR $result_dir/ diff --git a/tests/system/system_tests.cfg b/tests/system/system_tests.cfg index 8c295aacc..a03d8a7c7 100644 --- a/tests/system/system_tests.cfg +++ b/tests/system/system_tests.cfg @@ -225,3 +225,25 @@ driver = image global_parameter_file = global.image.STEHE.allhistvars.txt expected_retval = 0 check = nonans + +[System-mpi_image_check_identical_results] +test_description = check that multi-processor runs produce identical results - image driver +driver = image +global_parameter_file = global.image.STEHE.mpi.txt +expected_retval = 0 +check = mpi +[[mpi]] +# A list of number of processors to run and compare (need at least a list of two numbers) +n_proc = 1,4 + +[System-drivers_match] +test_description = Test whether classic driver and image driver produce similar results +driver = classic,image +# A list of global parameter template files; in the order corresponding to "driver" specified above +global_parameter_file = global.classic.STEHE.txt,global.image.STEHE.txt +expected_retval = 0 +check = driver_match +[[options]] +FULL_ENERGY=FALSE +FROZEN_SOIL=FALSE + diff --git a/tests/test_image_driver.py b/tests/test_image_driver.py index ef0f7380c..af8746b84 100644 --- a/tests/test_image_driver.py +++ b/tests/test_image_driver.py @@ -7,6 +7,8 @@ import pandas as pd import numpy as np import numpy.testing as npt +import glob +import warnings def test_image_driver_no_output_file_nans(fnames, domain_file): @@ -133,3 +135,165 @@ def reindex_xr_obj_timedim(obj, freq): print('actual=%s\nexpected=%s' % (actual, expected)) print(np.abs(actual-expected).max()) raise e + + +def setup_subdirs_and_fill_in_global_param_mpi_test( + s, list_n_proc, result_basedir, state_basedir, test_data_dir): + ''' Fill in global parameter output directories for multiple runs for mpi + testing, image driver + + Parameters + ---------- + s: + Template of the global param file to be filled in + list_n_proc: + A list of number of processors to run and compare + result_basedir: + Base directory of output fluxes results; runs with different number of + processors are output to subdirectories under the base directory + state_basedir: + Base directory of output state results; runs with different number of + processors are output to subdirectories under the base directory + test_data_dir: + Base directory of test data + + Returns + ---------- + list_global_param: + A list of global parameter strings to be run with parameters filled in + + Require + ---------- + os + ''' + + list_global_param = [] + for j, n_proc in enumerate(list_n_proc): + # Set up subdirectories for results and states + result_dir = os.path.join( + result_basedir, + 'processors_{}'.format(n_proc)) + state_dir = os.path.join( + state_basedir, + 'processors_{}'.format(n_proc)) + os.makedirs(result_dir, exist_ok=True) + os.makedirs(state_dir, exist_ok=True) + + # Fill in global parameter options + list_global_param.append(s.safe_substitute( + test_data_dir=test_data_dir, + result_dir=result_dir, + state_dir=state_dir)) + + return(list_global_param) + + +def check_mpi_fluxes(result_basedir, list_n_proc): + ''' Check whether all the fluxes are the same with different number of + processors, image driver + + Parameters + ---------- + result_basedir: + Base directory of output fluxes results; runs with different number of + processors are output to subdirectories under the base directory + list_n_proc: + A list of number of processors to run and compare + + Require + ---------- + os + glob + numpy + warnings + ''' + + # Read the first run - as base + n_proc = list_n_proc[0] + result_dir = os.path.join( + result_basedir, + 'processors_{}'.format(n_proc)) + if len(glob.glob(os.path.join(result_dir, '*.nc'))) > 1: + warnings.warn( + 'More than one netCDF file found under directory {}'. + format(result_dir)) + fname = glob.glob(os.path.join(result_dir, '*.nc'))[0] + ds_first_run = xr.open_dataset(fname) + + # Loop over all rest runs and compare fluxes with the base run + for i, n_proc in enumerate(list_n_proc): + # Skip the first run + if i == 0: + continue + # Read flux results for this run + result_dir = os.path.join( + result_basedir, + 'processors_{}'.format(n_proc)) + if len(glob.glob(os.path.join(result_dir, '*.nc'))) > 1: + warnings.warn( + 'More than one netCDF file found under directory {}'. + format(result_dir)) + fname = glob.glob(os.path.join(result_dir, '*.nc'))[0] + ds_current_run = xr.open_dataset(fname) + # Compare current run with base run + for var in ds_first_run.data_vars: + npt.assert_array_equal( + ds_current_run[var].values, + ds_first_run[var].values, + err_msg='Fluxes are not an exact match') + + +def check_mpi_states(state_basedir, list_n_proc): + ''' Check whether all the output states are the same with different number + of processors, image driver + + Parameters + ---------- + state_basedir: + Base directory of output states; runs with different number of + processors are output to subdirectories under the base directory + list_n_proc: + A list of number of processors to run and compare + + Require + ---------- + os + glob + numpy + warnings + ''' + + # Read the first run - as base + n_proc = list_n_proc[0] + state_dir = os.path.join( + state_basedir, + 'processors_{}'.format(n_proc)) + if len(glob.glob(os.path.join(state_dir, '*.nc'))) > 1: + warnings.warn( + 'More than one netCDF file found under directory {}'. + format(state_dir)) + fname = glob.glob(os.path.join(state_dir, '*.nc'))[0] + ds_first_run = xr.open_dataset(fname) + + # Loop over all rest runs and compare fluxes with the base run + for i, n_proc in enumerate(list_n_proc): + # Skip the first run + if i == 0: + continue + # Read output states for this run + state_dir = os.path.join( + state_basedir, + 'processors_{}'.format(n_proc)) + if len(glob.glob(os.path.join(state_dir, '*.nc'))) > 1: + warnings.warn( + 'More than one netCDF file found under directory {}'. + format(result_dir)) + fname = glob.glob(os.path.join(state_dir, '*.nc'))[0] + ds_current_run = xr.open_dataset(fname) + # Compare current run with base run + for var in ds_first_run.data_vars: + npt.assert_array_equal( + ds_current_run[var].values, + ds_first_run[var].values, + err_msg='States are not an exact match') + diff --git a/tests/test_restart.py b/tests/test_restart.py index 7501ce825..dbf13a715 100644 --- a/tests/test_restart.py +++ b/tests/test_restart.py @@ -6,6 +6,7 @@ import os import glob import xarray as xr +import warnings from test_utils import read_vic_ascii from tonic.testing import VICTestError @@ -212,6 +213,14 @@ def check_exact_restart_fluxes(result_basedir, driver, run_periods): run_periods: A list of running periods. Return from prepare_restart_run_periods() + Require: + ---------- + xarray + glob + os + numpy + warnings + read_vic_ascii ''' # --- Extract full run period --- # @@ -231,9 +240,9 @@ def check_exact_restart_fluxes(result_basedir, driver, run_periods): dict_df_full_run[os.path.basename(fname)] = df elif driver == 'image': if len(glob.glob(os.path.join(result_dir, '*.nc'))) > 1: - print( - 'Warning: more than one netCDF file found under directory {}'. - format(result_dir)) + warnings.warn( + 'More than one netCDF file found under directory {}'. + format(result_dir)) fname = glob.glob(os.path.join(result_dir, '*.nc'))[0] ds_full_run = xr.open_dataset(fname) @@ -266,8 +275,8 @@ def check_exact_restart_fluxes(result_basedir, driver, run_periods): elif driver == 'image': # Read in flux data if len(glob.glob(os.path.join(result_dir, '*.nc'))) > 1: - print('Warning: more than one netCDF file found under' - 'directory {}'.format(result_dir)) + warnings.warn('More than one netCDF file found under' + 'directory {}'.format(result_dir)) fname = glob.glob(os.path.join(result_dir, '*.nc'))[0] ds = xr.open_dataset(fname) # Extract the same period from the full run diff --git a/tests/test_utils.py b/tests/test_utils.py index 73a45f5a4..a55aaad95 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,13 +1,20 @@ -#!/usr/bin/env python -''' VIC exact restart testing ''' - import os -from collections import OrderedDict +from collections import OrderedDict, namedtuple +import glob import traceback import numpy as np import pandas as pd +import xarray as xr +import glob +import re +import matplotlib +matplotlib.use('Agg') +import matplotlib.pyplot as plt +import warnings +import seaborn as sns +import multiprocessing as mp from tonic.models.vic.vic import (VICRuntimeError, default_vic_valgrind_error_code) @@ -16,6 +23,8 @@ OUTPUT_WIDTH = 100 ERROR_TAIL = 20 # lines +VICOutFile = namedtuple('vic_out_file', + ('dirpath', 'prefix', 'lat', 'lon', 'suffix')) class VICReturnCodeError(Exception): pass @@ -59,7 +68,10 @@ def clip_string(string, length=50): def print_tail(string, n=20, indent='\t--->'): '''print tail of multiline string''' - lines = string.decode().splitlines() + try: + lines = string.decode().splitlines() + except UnicodeDecodeError: + lines = string.splitlines() for l in lines[-n:]: print('{0}{1}'.format(indent, l)) @@ -92,15 +104,35 @@ def replace_global_values(gp, replace): def drop_tests(config, driver): - '''helper function to remove tests that should not be run for driver''' - new = {} - for key, test_cfg in config.items(): - try: - if test_cfg['driver'].lower() == driver.lower(): - new[key] = test_cfg - except KeyError: - raise KeyError('test configuration must specify driver') - return new + '''helper function to remove tests that should not be run for driver''' + + new = {} + + if not isinstance(driver, list): # if single driver + for key, test_cfg in config.items(): + try: + if not isinstance(test_cfg['driver'], list): + if test_cfg['driver'].lower() == driver.lower(): + new[key] = test_cfg + except KeyError: + raise KeyError('test configuration must specify driver') + else: # if multiple drivers + for key, test_cfg in config.items(): + try: + if isinstance(test_cfg['driver'], list): + # check whether the test has the same number of drivers + if len(test_cfg['driver']) == len(driver): + # check whether the test wants to test the same drivers + flag = 1 + for d in driver: + if d not in test_cfg['driver']: + flag = 0 + if flag == 1: + new[key] = test_cfg + except KeyError: + raise KeyError('test configuration must specify driver') + + return new def pop_run_kwargs(config): @@ -117,7 +149,8 @@ def check_returncode(exe, expected=0): return None elif exe.returncode == default_vic_valgrind_error_code: raise VICValgrindError( - 'Valgrind raised an error when running: "{}"'.format(exe.argstring)) + 'Valgrind raised an error when running: \ + "{}"'.format(exe.argstring)) else: raise VICReturnCodeError( 'VIC return code ({0}) did not match expected ({1}) when running ' @@ -174,11 +207,13 @@ def test_classic_driver_all_complete(fnames): def test_classic_driver_no_output_file_nans(fnames): '''Test that all VIC classic driver output files in fnames have no nans''' for fname in fnames: + print(fname) df = read_vic_ascii(fname) check_for_nans(df) -# TODO: Update tonic version of this function, need to check that subdaily works +# TODO: Update tonic version of this function, +# need to check that subdaily works def read_vic_ascii(filepath, parse_dates=True, datetime_index=None, sep='\t', comment='#', **kwargs): '''Generic reader function for VIC ASCII output with a standard header @@ -199,13 +234,15 @@ def read_vic_ascii(filepath, parse_dates=True, datetime_index=None, sep='\t', raise ValueError('cannot specify both parse_dates and datetime_index') if parse_dates: + # add datetime index time_cols = ['YEAR', 'MONTH', 'DAY'] df.index = pd.to_datetime(df[time_cols]) if 'SEC' in df: - df.index += pd.Series([pd.Timedelta(s, unit='s') for s in df['SEC']], + df.index += pd.Series([pd.Timedelta(s, unit='s') for s in + df['SEC']], index=df.index) time_cols.append('SEC') - df.drop(time_cols, axis=1) + df.drop(time_cols, inplace=True, axis=1) if datetime_index is not None: df.index = datetime_index @@ -239,8 +276,7 @@ def find_global_param_value(gp, param_name): def check_multistream_classic(fnames): ''' - Test the multistream aggregation in the classic driver - ''' + Test the multistream aggregation in the classic driver ''' how_dict = {'OUT_ALBEDO': 'max', 'OUT_SOIL_TEMP_1': 'min', @@ -280,12 +316,14 @@ def check_multistream_classic(fnames): # Loop over all grid cells in result dir for gridcell in gridcells: - fname = os.path.join(resultdir, '{}_{}.txt'.format(inst_stream, gridcell)) + fname = os.path.join(resultdir, + '{}_{}.txt'.format(inst_stream, gridcell)) instant_df = read_vic_ascii(fname) # Loop over all streams for stream, freq in streams.items(): - fname = os.path.join(resultdir, '{}_{}.txt'.format(stream, gridcell)) + fname = os.path.join(resultdir, + '{}_{}.txt'.format(stream, gridcell)) agg_df = read_vic_ascii(fname) # Setup the resample of the instantaneous data @@ -295,7 +333,8 @@ def check_multistream_classic(fnames): for key, how in how_dict.items(): # Get the aggregated values (from VIC) actual = agg_df[key].values - # Calculated the expected values based on the resampling from pandas + # Calculated the expected values based on the resampling from + # pandas expected = rs[key].aggregate(how).values # Compare the actual and expected (with tolerance) @@ -303,3 +342,711 @@ def check_multistream_classic(fnames): actual, expected, decimal=4, err_msg='Variable=%s, freq=%s, how=%s: ' 'failed comparison' % (key, freq, how)) + + +def setup_subdirs_and_fill_in_global_param_driver_match_test( + dict_s, result_basedir, state_basedir, test_data_dir): + ''' Fill in global parameter output directories for multiple driver runs + for driver-match testing + + Parameters + ---------- + dict_s: + A dict of template of the global param file to be filled in + Keys: driver name + result_basedir: + Base directory of output fluxes results; runs with different number of + processors are output to subdirectories under the base directory + state_basedir: + Base directory of output state results; runs with different number of + processors are output to subdirectories under the base directory + test_data_dir: + Base directory of test data + + Returns + ---------- + dict_global_param: + A dict of global parameter strings to be run with parameters filled in + + Require + ---------- + os + ''' + + dict_global_param = {} + for driver in dict_s.keys(): + # Set up subdirectories for results and states + result_dir = os.path.join(result_basedir, driver) + state_dir = os.path.join(state_basedir, driver) + os.makedirs(result_dir, exist_ok=True) + os.makedirs(state_dir, exist_ok=True) + + # Fill in global parameter options + s = dict_s[driver] + dict_global_param[driver] = s.safe_substitute( + test_data_dir=test_data_dir, + result_dir=result_dir, + state_dir=state_dir) + + return(dict_global_param) + + +def parse_classic_driver_outfile_name(fname): + '''helper function to parse VIC classic driver output file name''' + resultdir, filename = os.path.split(fname) + prefix, suffix = os.path.splitext(filename) + pieces = prefix.split('_') + lat, lon = map(float, pieces[-2:]) + return VICOutFile(resultdir, prefix, lat, lon, suffix) + + +def check_drivers_match_fluxes(list_drivers, result_basedir): + ''' Check whether the flux results are similar cross multiple drivers + + Parameters + ---------- + list_drivers: + A list of driver names to be compared + e.g., ['classic'; 'image'] + NOTE: must have classic driver; classic driver will be the base for + comparison + result_basedir: + Base directory of output fluxes results; results for drivers are + subdirectories under the base directory + + Require + ---------- + glob + xarray + numpy + warnings + collections.namedtuple + parse_classic_driver_outfile_name + VICOutFile + read_vic_ascii + ''' + + # Identify all classic driver output flux files + try: + list_fnames_classic = glob.glob(os.path.join( + result_basedir, 'classic', '*')) + except: + raise ValueError('incorrect classic driver output for driver-match ' + 'test') + + # Loop over all other drivers and compare with classic driver + for driver in list_drivers: + # skip classic driver + if driver == 'classic': + continue + # if image driver + if driver == 'image': + # load flux file + if len(glob.glob(os.path.join( + result_basedir, driver, '*.nc'))) > 1: + warnings.warn('More than one netCDF file found under' + 'directory {}'.format(result_dir)) + fname = glob.glob(os.path.join(result_basedir, driver, '*.nc'))[0] + ds_image = xr.open_dataset(fname) + + # loop over each grid cell from classic driver + for fname in list_fnames_classic: + gcell = parse_classic_driver_outfile_name(fname) + df_classic = read_vic_ascii(fname) + ds_image_cell = ds_image.sel(lat=gcell.lat, lon=gcell.lon, + method='nearest') + # compare each variable + for var in ds_image_cell.data_vars: + # if one [time] dimension + if len(ds_image_cell[var].coords) == 3: + # determine precision for comparison + # --- if all zeros for this variable, set + # --- decimal = 2 --- # + if np.sum(np.absolute(ds_image_cell[var].values)) == 0: + decimal = 2 + # --- if not all zeros, set decimal depending on the + # maximum aboslute value of this variable so that the + # comparison has a reasonable precision. Specifically, + # decimal ~= - log10(max_abs_value) + 1 --- # + else: + decimal = int(round(- np.log10(np.max(np.absolute( + ds_image_cell[var].values))) + 1)) + # --- keep decimal to be no greater than 4 --- # + if decimal > 4: + decimal = 4 + # assert almost equal + np.testing.assert_almost_equal( + ds_image_cell[var].values, df_classic[var].values, + decimal=decimal, + err_msg='Variable {} is different in the classic ' + 'and image drivers'.format(var)) + # if [time, nlayer] + elif len(ds_image_cell[var].coords) == 4: + for l in ds_image['nlayer']: + s_classic = df_classic['{}_{}'.format(var, l.values)] + s_image = ds_image_cell[var].sel(nlayer=l).to_series() + # determine precision for comparison + if np.mean(s_image.values) == 0: + decimal = 2 + else: + decimal = int(round(- np.log10(np.max( + np.absolute(s_image.values))) + 1)) + if decimal > 4: + decimal = 4 + # assert almost eqaul + np.testing.assert_almost_equal( + s_image.values, + s_classic.values, + decimal=decimal, + err_msg='Variable {} is different in the ' + 'classic and image drivers'. + format(var)) + + +def tsplit(string, delimiters): + '''Behaves like str.split but supports multiple delimiters. ''' + + delimiters = tuple(delimiters) + stack = [string] + + for delimiter in delimiters: + for i, substring in enumerate(stack): + substack = substring.split(delimiter) + stack.pop(i) + for j, _substring in enumerate(substack): + stack.insert(i+j, _substring) + + return stack + + +def read_snotel_swe_obs(filename, science_test_data_dir, items): + '''Reads in Snotel SWE obs and returns DataFrame. ''' + + filename_fullpath = os.path.join(science_test_data_dir, + 'science', + 'inputdata', + items['archive'], + 'observations', + filename) + + # load snotel obs + snotel_swe = pd.read_csv(filename_fullpath, + skiprows=0, + delim_whitespace=True, + names=['YEAR', 'MONTH', 'DAY', 'OUT_SWE']) + + # add datetime index + time_cols = ['YEAR', 'MONTH', 'DAY'] + snotel_swe.index = pd.to_datetime(snotel_swe[time_cols]) + + # remove year, day columns of DataFrame + snotel_swe.drop(time_cols, inplace=True, axis=1) + + return snotel_swe + + +def read_vic_42_output(lat, lng, science_test_data_dir, items): + ''' Reads output from VIC 4.2. ''' + + if items['compare_to'] == 'ecflux': + vic_42_file = 'en_bal_%s_%s' % (lat, lng) + vic_42_dir = os.path.join(science_test_data_dir, 'science', 'archive', + items['archive'], 'ecflux', 'results') + + elif items['compare_to'] == 'snotel': + vic_42_file = 'outfile_%s_%s' % (lat, lng) + vic_42_dir = os.path.join(science_test_data_dir, 'science', 'archive', + items['archive'], 'snotel', 'results') + + else: + raise ValueError("this option (%s) has not yet been implemented" + % items['compare_to']) + + vic_42 = pd.read_csv(os.path.join(vic_42_dir, vic_42_file), + sep='\t', + skiprows=5) + + # remove comment sign from column names in DataFrame + vic_42 = vic_42.rename(columns=lambda x: x.replace('#', '')) + + # remove spaces from column names in DataFrame + vic_42 = vic_42.rename(columns=lambda x: x.replace(' ', '')) + + # rename radiation variables to be consistent with VIC 5 + if items['compare_to'] == 'ecflux': + vic_42 = vic_42.rename(columns=lambda x: x.replace('OUT_NET_SHORT', + 'OUT_SWNET')) + vic_42 = vic_42.rename(columns=lambda x: x.replace('OUT_NET_LONG', + 'OUT_LWNET')) + + # add datetime index + time_cols = ['YEAR', 'MONTH', 'DAY'] + vic_42.index = pd.to_datetime(vic_42[time_cols]) + + if 'HOUR' in vic_42: + vic_42.index += pd.Series([pd.Timedelta(s, unit='h') for s in + vic_42['HOUR']], + index=vic_42.index) + time_cols.append('HOUR') + + # remove year, day columns of DataFrame + vic_42.drop(time_cols, inplace=True, axis=1) + + return vic_42 + + +def read_vic_5_output(lat, lng, result_dir, items): + ''' Read VIC 5.0.x output. ''' + + if items['compare_to'] == 'ecflux': + vic_5_file = 'en_bal_%s_%s.txt' % (lat, lng) + vic_5_dir = result_dir + + elif items['compare_to'] == 'snotel': + vic_5_file = 'outfile_%s_%s.txt' % (lat, lng) + vic_5_dir = result_dir + + else: + raise ValueError("this option (%s) has not yet been implemented" + % items['compare_to']) + + vic_5 = pd.read_csv(os.path.join(vic_5_dir, vic_5_file), + skiprows=2, + sep='\t') + + # remove spaces from column names + vic_5.rename(columns=lambda x: x.replace(' ', ''), inplace=True) + + # add datetime index + time_cols = ['YEAR', 'MONTH', 'DAY'] + vic_5.index = pd.to_datetime(vic_5[time_cols]) + + if 'SEC' in vic_5: + vic_5.index += pd.Series([pd.Timedelta(s, unit='s') + for s in vic_5['SEC']], index=vic_5.index) + time_cols.append('SEC') + + # remove year, day columns of DataFrame + vic_5.drop(time_cols, inplace=True, axis=1) + + return vic_5 + + +def plot_science_tests(driver, test_type, science_test_data_dir, result_dir, + plot_dir, plots_to_make, compare_data, nproc): + + ''' makes science test figures + + Parameters + ---------- + driver: + Name of Driver + test_type: + Name of test + science_test_data_dir: + Science test data directory + result_dir: + Result directory + plot_dir: + Directory for output plots + plots_to_make + Keys that indicate which plots should be made + compare_data + Keys that indicate which datasets and model output to use for + comparison. + nproc + Number of processors to use + + Returns + ---------- + ''' + if test_type == "science_test_snotel": + plot_snotel_comparison(driver, + science_test_data_dir, + compare_data, + result_dir, + plot_dir, + plots_to_make, + nproc) + + elif test_type == "science_test_fluxnet": + plot_fluxnet_comparison(driver, + science_test_data_dir, + compare_data, + result_dir, + plot_dir, + plots_to_make, + nproc) + else: + raise ValueError("this option %s has not been implemented in the \ + VIC 5.0 science test suite" % test_type) + + +def plot_snotel_comparison(driver, science_test_data_dir, + compare_data_dict, + result_dir, plot_dir, + plots_to_make, nproc): + ''' makes snotel figures ''' + + # plot settings + plot_variables = {'OUT_SWE': 'mm', 'OUT_ALBEDO': 'fraction', + 'OUT_SALBEDO': 'fraction', 'OUT_SNOW_DEPTH': 'mm', + 'OUT_SNOW_CANOPY': '%', 'OUT_SNOW_PACK_TEMP': + 'degrees C', 'OUT_SNOW_MELT': 'mm', 'OUT_R_NET': + '$W/{m^2}$', 'OUT_LATENT': '$W/{m^2}$', + 'OUT_SENSIBLE': '$W/{m^2}$'} + context = "paper" + style = "whitegrid" + + # --- Set up multiprocessing --- # + pool = mp.Pool(processes=nproc) + + for filename in os.listdir(os.path.join(science_test_data_dir, + 'science', + 'inputdata', + 'snotel', + 'observations')): + pool.apply_async(plot_snotel_comparison_one_site, + (driver, science_test_data_dir, + compare_data_dict, + result_dir, plot_dir, + plots_to_make, + plot_variables, context, style, filename,)) + + # --- Finish multiprocessing --- # + pool.close() + pool.join() + + +def plot_snotel_comparison_one_site( + driver, science_test_data_dir, + compare_data_dict, + result_dir, plot_dir, + plots_to_make, + plot_variables, context, style, filename): + + # get lat/lng from filename + file_split = re.split('_', filename) + lng = file_split[3].split('.txt')[0] + lat = file_split[2] + print('Plotting {} {}'.format(lat, lng)) + + # loop over data to compare + data = {} + for key, items in compare_data_dict.items(): + + # read in data + if key == "snotel": + data[key] = read_snotel_swe_obs(filename, + science_test_data_dir, + items) + + elif key == "VIC.4.2.d": + data[key] = read_vic_42_output(lat, lng, + science_test_data_dir, + items) + + else: + data[key] = read_vic_5_output(lat, lng, + result_dir, + items) + + # loop over variables to plot + for plot_variable, units in plot_variables.items(): + + if 'water_year' in plots_to_make: + + with plt.rc_context(dict(sns.axes_style(style), + **sns.plotting_context(context))): + fig, ax = plt.subplots(figsize=(10, 10)) + + df = pd.DataFrame({key: d[plot_variable] for key, d in + data.items() if plot_variable in d}) + + for key, series in df.iteritems(): + series.plot( + use_index=True, + linewidth=compare_data_dict[key]['linewidth'], + ax=ax, + color=compare_data_dict[key]['color'], + linestyle=compare_data_dict[key] + ['linestyle'], + zorder=compare_data_dict[key]['zorder']) + + ax.legend(loc='upper left') + ax.set_ylabel("%s [%s]" % (plot_variable, units)) + + # save figure + os.makedirs(os.path.join(plot_dir, plot_variable), + exist_ok=True) + plotname = '%s_%s.png' % (lat, lng) + savepath = os.path.join(plot_dir, plot_variable, plotname) + plt.savefig(savepath, bbox_inches='tight') + + plt.clf() + plt.close() + + +def check_site_files(obs_dir, subdir): + return len(os.listdir(os.path.join(obs_dir, subdir))) > 0 + + +def get_fluxnet_lat_lon(obs_dir, subdir): + + # get CSV file from site directory to get lat/lng for site + try: + site_csv_file = glob.glob(os.path.join(obs_dir, subdir, 'AMF*.csv'))[0] + except IndexError: + site_csv_file = glob.glob(os.path.join(obs_dir, subdir, 'us*.csv'))[0] + with open(site_csv_file) as f: + second_line = list(f)[1] + + # parse line from header to get lat/lng + str_split = tsplit(second_line, + ('Latitude: ', 'Longitude: ', 'Elevation (masl): ')) + lat = str_split[1].strip() + lng = str_split[2].strip() + + return lat, lng + + +def read_fluxnet_obs(subdir, science_test_data_dir, items): + + # column names for DataFrame (same as VIC variable names) + fluxnet_names = ['YEAR', 'MONTH', 'DAY', 'HOUR', 'PREC', 'AIR_TEMP', + 'SWDOWN', 'LWDOWN', 'OUT_REL_HUMID', 'PRESSURE', 'WIND', + 'OUT_EVAP', 'SOIL_TEMP_DEPTH1', 'SOIL_TEMP_DEPTH2', + 'SOIL_TEMP_DEPTH3', 'SOIL_TEMP_DEPTH4', + 'SOIL_TEMP_DEPTH5', 'OUT_SOIL_MOIST1', 'OUT_SOIL_MOIST2', + 'OUT_SOIL_MOIST3', 'OUT_SOIL_MOIST4', 'OUT_SOIL_MOIST5', + 'OUT_SOIL_TEMP1', 'OUT_SOIL_TEMP2', 'OUT_SOIL_TEMP3', + 'OUT_SOIL_TEMP4', 'OUT_SOIL_TEMP5', 'OUT_SWNET', + 'OUT_LWNET', 'OUT_SENSIBLE', 'OUT_LATENT', + 'OUT_GRND_FLUX'] + + filename = '%s.stdfmt.hourly.local.txt' % subdir + # read in data with -9999.0000 as NaNs + obs_dir = os.path.join(science_test_data_dir, 'science', 'inputdata', + 'ec_flux_towers', 'obs') + ecflux_df = pd.read_csv(os.path.join(obs_dir, subdir, filename), + skiprows=0, + delim_whitespace=True, + header=None, + names=fluxnet_names, + na_values=-9999.0000) + + # add datetime index + time_cols = ['YEAR', 'MONTH', 'DAY'] + ecflux_df.index = pd.to_datetime(ecflux_df[time_cols]) + + if 'HOUR' in ecflux_df: + ecflux_df.index += pd.Series([pd.Timedelta(s, unit='h') for s in + ecflux_df['HOUR']], + index=ecflux_df.index) + time_cols.append('HOUR') + + # remove year, day columns of DataFrame + ecflux_df.drop(time_cols, inplace=True, axis=1) + + return ecflux_df + + +def plot_fluxnet_comparison(driver, science_test_data_dir, + compare_data_dict, + result_dir, plot_dir, + plots_to_make, nproc): + ''' makes Ameriflux figures + ''' + + context = "paper" + style = "whitegrid" + var_names = {'OUT_LATENT': 'LH', 'OUT_SENSIBLE': 'H', 'OUT_SWNET': + 'SW_NET', 'OUT_LWNET': 'LW NET'} + + months = ['January', 'February', 'March', 'April', 'May', + 'June', 'July', 'August', 'September', + 'October', 'November', 'December'] + + # loop over Ameriflux sites + obs_dir = os.path.join(science_test_data_dir, + 'science', + 'inputdata', + 'ec_flux_towers', + 'obs') + + # --- Set up multiprocessing --- # + pool = mp.Pool(processes=nproc) + + for subdir in os.listdir(obs_dir): + pool.apply_async(plot_fluxnet_comparison_one_site, + (driver, science_test_data_dir, + compare_data_dict, + result_dir, plot_dir, + plots_to_make, + context, style, var_names, months, obs_dir, subdir,)) + + # --- Finish multiprocessing --- # + pool.close() + pool.join() + + +def plot_fluxnet_comparison_one_site( + driver, science_test_data_dir, + compare_data_dict, + result_dir, plot_dir, + plots_to_make, + context, style, var_names, months, obs_dir, subdir): + + if check_site_files(obs_dir, subdir): + # get CSV file from site directory to get lat/lng for site + lat, lng = get_fluxnet_lat_lon(obs_dir, subdir) + print(lat, lng) + + # loop over data to compare + data = {} + for key, items in compare_data_dict.items(): + + if key == "ecflux": + try: + # load Ameriflux data + data[key] = read_fluxnet_obs(subdir, + science_test_data_dir, + items) + except OSError: + warnings.warn( + "this %s site does not have data" % subdir) + + elif key == "VIC.4.2.d": + try: + # load VIC 4.2 simulations + data[key] = read_vic_42_output(lat, lng, + science_test_data_dir, + items) + + except OSError: + warnings.warn( + "this site has a lat/lng precision issue") + + else: + try: + # load VIC 5 simulations + data[key] = read_vic_5_output(lat, lng, + result_dir, + items) + except OSError: + warnings.warn( + "this site has a lat/lng precision issue") + + # make figures + + # plot preferences + fs = 15 + dpi = 150 + + if 'annual_mean_diurnal_cycle' in plots_to_make: + + # make annual mean diurnal cycle plots + with plt.rc_context(dict(sns.axes_style(style), + **sns.plotting_context(context))): + f, axarr = plt.subplots(4, 1, figsize=(8, 8), sharex=True) + + for i, (vic_var, variable_name) in enumerate( + var_names.items()): + + # calculate annual mean diurnal cycle for each + # DataFrame + annual_mean = {} + for key, df in data.items(): + annual_mean[key] = pd.DataFrame( + df[vic_var].groupby(df.index.hour).mean()) + + df = pd.DataFrame( + {key: d[vic_var] for key, d in annual_mean.items() + if vic_var in d}) + + for key, series in df.iteritems(): + series.plot( + linewidth=compare_data_dict[key]['linewidth'], + ax=axarr[i], + color=compare_data_dict[key]['color'], + linestyle=compare_data_dict[key]['linestyle'], + zorder=compare_data_dict[key]['zorder']) + + axarr[i].legend(loc='upper left') + axarr[i].set_ylabel( + '%s ($W/{m^2}$)' % variable_name, + size=fs) + axarr[i].set_xlabel('Time of Day (Hour)', size=fs) + axarr[i].set_xlim([0, 24]) + axarr[i].xaxis.set_ticks(np.arange(0, 24, 3)) + + # save plot + plotname = '%s_%s.png' % (lat, lng) + os.makedirs(os.path.join(plot_dir, 'annual_mean'), + exist_ok=True) + savepath = os.path.join(plot_dir, 'annual_mean', plotname) + plt.savefig(savepath, bbox_inches='tight', dpi=dpi) + + plt.clf() + plt.close() + + if 'monthly_mean_diurnal_cycle' in plots_to_make: + + # make monthly mean diurnal cycle plots + with plt.rc_context(dict(sns.axes_style(style), + **sns.plotting_context(context))): + f, axarr = plt.subplots(4, 12, figsize=(35, 7), + sharex=True, + sharey=True) + + for i, (vic_var, variable_name) in enumerate( + var_names.items()): + + # calculate monthly mean diurnal cycle + monthly_mean = {} + for (key, df) in data.items(): + monthly_mean[key] = pd.DataFrame( + df[vic_var].groupby([df.index.month, + df.index.hour]).mean()) + + df = pd.DataFrame( + {key: d[vic_var] for key, d in monthly_mean.items() + if vic_var in d}) + + for j, month in enumerate(months): + + for key, series in df.iteritems(): + series[j + 1].plot( + linewidth=compare_data_dict[key]['linewidth'], + ax=axarr[i, j], + color=compare_data_dict[key]['color'], + linestyle=compare_data_dict[key]['linestyle'], + zorder=compare_data_dict[key]['zorder']) + + axarr[i, j].set_ylabel( + '%s \n ($W/{m^2}$)' % variable_name, + size=fs) + axarr[i, j].set_xlabel('', size=fs) + axarr[i, j].set_xlim([0, 24]) + axarr[i, j].xaxis.set_ticks(np.arange(0, 24, 3)) + if i == 0: + axarr[i, j].set_title(month, size=fs) + + # add legend + axarr[0, -1].legend(loc='center left', + bbox_to_anchor=(1, 0.5)) + + # add common x label + f.text(0.5, 0.04, 'Time of Day (Hour)', ha='center', + size=fs) + + # save plot + plotname = '%s_%s.png' % (lat, lng) + os.makedirs(os.path.join(plot_dir, 'monthly_mean'), + exist_ok=True) + savepath = os.path.join(plot_dir, + 'monthly_mean', plotname) + plt.savefig(savepath, bbox_inches='tight', dpi=dpi) + + plt.clf() + plt.close() diff --git a/tests/unit/shared/test_initialize_files.py b/tests/unit/shared/test_initialize_files.py deleted file mode 100644 index 6ff1d65f6..000000000 --- a/tests/unit/shared/test_initialize_files.py +++ /dev/null @@ -1,11 +0,0 @@ -from vic import lib as vic_lib -from vic import ffi - - -def test_initialize_filenames(): - assert vic_lib.initialize_filenames() is None - assert ffi.string(vic_lib.filenames.init_state) == b'MISSING' - - -def test_initialize_fileps(): - assert vic_lib.initialize_fileps() is None diff --git a/tests/unit/shared/test_print_library_shared.py b/tests/unit/shared/test_print_library_shared.py index 76a1e5365..50147cbae 100644 --- a/tests/unit/shared/test_print_library_shared.py +++ b/tests/unit/shared/test_print_library_shared.py @@ -2,13 +2,6 @@ from vic import lib as vic_lib, ffi -# TODO: fix make_all_vars struct - -# @pytest.fixture() -# def all_data(scope='function'): -# return vic_lib.make_all_vars(4) - - @pytest.fixture() def dmy(scope='function'): d = ffi.new('dmy_struct *') @@ -20,27 +13,10 @@ def dmy(scope='function'): return d -# def test_print_all_data(all_data): -# assert vic_lib.print_cell_data(all_data.cell, vic_lib.options.Nlayer, -# vic_lib.options.Nfrost, 0) is None - - def test_print_dmy(dmy): assert vic_lib.print_dmy(dmy) is None -# def test_print_energy_bal(all_data): -# assert vic_lib.print_energy_bal(all_data.energy, 1, 1) is None - - -def test_print_filenames(): - assert vic_lib.print_filenames(ffi.addressof(vic_lib.filenames)) is None - - -def test_print_filep(): - assert vic_lib.print_filep(ffi.addressof(vic_lib.filep)) is None - - def test_print_global_param(): assert vic_lib.print_global_param( ffi.addressof(vic_lib.global_param)) is None @@ -52,7 +28,3 @@ def test_print_option(): def test_print_parameters(): assert vic_lib.print_parameters(ffi.addressof(vic_lib.param)) is None - - -# def test_print_snow_data(all_data): -# assert vic_lib.print_snow_data(all_data.snow) is None diff --git a/tests/unit/shared/test_timing.py b/tests/unit/shared/test_timing.py new file mode 100644 index 000000000..6856a4e01 --- /dev/null +++ b/tests/unit/shared/test_timing.py @@ -0,0 +1,37 @@ +import time + +from vic.vic import ffi +from vic import lib as vic_lib + + +def test_vic_timers(): + + timer = ffi.new('timer_struct *') + sleeptime = 1.5 + delta = 0.1 + + # init sets delta to zero + vic_lib.timer_init(timer) + + # start gets current time + vic_lib.timer_start(timer) + + # sleep + time.sleep(sleeptime) + + # stop pauses the timer + vic_lib.timer_stop(timer) + assert timer[0].delta_wall >= sleeptime + assert timer[0].delta_wall < sleeptime + delta + assert timer[0].delta_cpu >= 0. + + # start the timer again + vic_lib.timer_continue(timer) + + # sleep again + time.sleep(sleeptime) + + # stop after the lap time sleep + vic_lib.timer_stop(timer) + assert timer[0].delta_wall >= 2 * sleeptime + assert timer[0].delta_wall < 2 * (sleeptime + delta) diff --git a/tests/unit/shared/test_vic_log.py b/tests/unit/shared/test_vic_log.py index b3cea45dd..141619b42 100644 --- a/tests/unit/shared/test_vic_log.py +++ b/tests/unit/shared/test_vic_log.py @@ -1,6 +1,4 @@ -import pytest import datetime -import os from vic import lib as vic_lib from vic import ffi @@ -31,18 +29,3 @@ def test_get_logname(): def test_initialize_log(): assert vic_lib.initialize_log() is None - - -def test_setup_logging(): - assert vic_lib.setup_logging(1) is None - - -def test_setup_logging_to_file(tmpdir): - vic_lib.filenames.log_path = (str(tmpdir.mkdir('log')) + '/').encode() - num = 321 - assert vic_lib.setup_logging(num) is None - log_file = os.listdir(ffi.string(vic_lib.filenames.log_path))[0] - print(log_file) - assert log_file.startswith(b'vic.log') - assert log_file.endswith('{0}.txt'.format(num).encode()) - vic_lib.finalize_logging() diff --git a/tests/vic_valgrind_suppressions.supp b/tests/vic_valgrind_suppressions.supp new file mode 100644 index 000000000..7ad498fe9 --- /dev/null +++ b/tests/vic_valgrind_suppressions.supp @@ -0,0 +1,78 @@ +# +# This is a valgrind suppression file that should be used when using valgrind. +# It is used to keep valgrind from reporting errors on known errors in libraries +# VIC uses. +# +# Here's an example of running valgrind: +# +# cd vic/drivers/classic +# valgrind -v --leak-check=full --error-exitcode=125 \ +# --suppressions=vic_valgrind_supressions.supp vic_classic.exe -g global_param.txt +# +# all tool names: Addrcheck,Memcheck,cachegrind,helgrind,massif +# + +{ + supress_dispatch_not_vic_specific + Memcheck:Leak + # match-leak-kinds: definite + ... + fun:dispatch_once_f + ... +} +{ + supress_getpwuid1_not_vic_specific + Memcheck:Leak + # match-leak-kinds: definite + ... + fun:getpwuid + ... +} +{ + supress_localtime_not_vic_specific + Memcheck:Leak + # match-leak-kinds: definite + ... + fun:localtime + ... +} +{ + supress_bootstrap_not_vic_specific + Memcheck:Leak + # match-leak-kinds: definite + ... + fun:bootstrap_look_up3 + ... +} +{ + supress_sprintf_not_vic_specific + Memcheck:Leak + # match-leak-kinds: definite + ... + fun:__sprintf_chk + ... +} +{ + supress_sscanf_not_vic_specific + Memcheck:Leak + # match-leak-kinds: definite + ... + fun:sscanf + ... +} +{ + supress_stringcopy_not_vic_specific + Memcheck:Leak + # match-leak-kinds: possible + ... + fun:_ZL22copySwiftV1MangledNamePKcb + ... +} +{ + supress_getpwuid_r_not_vic_specific + Memcheck:Leak + # match-leak-kinds: definite + ... + fun:getpwuid_r + ... +} diff --git a/vic/drivers/cesm/bld/vic.globalconfig.txt b/vic/drivers/cesm/bld/vic.globalconfig.txt index dc11047c0..46bfe6b52 100644 --- a/vic/drivers/cesm/bld/vic.globalconfig.txt +++ b/vic/drivers/cesm/bld/vic.globalconfig.txt @@ -37,14 +37,12 @@ RC_MODE RC_JARVIS # Input Files and PATHS CONSTANTS {vic_constants} LOG_DIR {rundir}/ -SOIL {vic_params} -VEGPARAM {vic_params} +PARAMETERS {vic_params} LAI_SRC FROM_VEGPARAM ALB_SRC FROM_VEGPARAM FCAN_SRC FROM_DEFAULT -VEGLIB {vic_params} -SNOW_BAND 1 -RESULT_DIR {rundir}/vic.testing.h0.nc +SNOW_BAND FALSE +RESULT_DIR {rundir}/ # Domain File DOMAIN {vic_domain} @@ -56,8 +54,9 @@ DOMAIN_TYPE FRAC frac DOMAIN_TYPE YDIM nj DOMAIN_TYPE XDIM ni -N_OUTFILES OUTFILE fluxes +AGGFREQ NDAYS 1 +OUT_FORMAT NETCDF4_CLASSIC OUTVAR OUT_PREC OUTVAR OUT_RAINF OUTVAR OUT_SNOWF diff --git a/vic/drivers/cesm/src/cesm_interface_c.c b/vic/drivers/cesm/src/cesm_interface_c.c index d3bd5de6b..92224effd 100644 --- a/vic/drivers/cesm/src/cesm_interface_c.c +++ b/vic/drivers/cesm/src/cesm_interface_c.c @@ -31,7 +31,7 @@ size_t current; size_t *filter_active_cells = NULL; size_t *mpi_map_mapping_array = NULL; all_vars_struct *all_vars = NULL; -atmos_data_struct *atmos = NULL; +force_data_struct *force = NULL; x2l_data_struct *x2l_vic = NULL; l2x_data_struct *l2x_vic = NULL; dmy_struct dmy_current; @@ -67,6 +67,7 @@ save_data_struct *save_data; // [ncells] double ***out_data = NULL; // [ncells, nvars, nelem] stream_struct *output_streams = NULL; // [nstreams] nc_file_struct *nc_hist_files = NULL; // [nstreams] +timer_struct global_timers[N_TIMERS]; /****************************************************************************** * @brief Initialization function for CESM driver @@ -75,6 +76,11 @@ int vic_cesm_init(vic_clock *vclock, case_metadata *cmeta) { + // start vic all timer + timer_start(&(global_timers[TIMER_VIC_ALL])); + // start vic init timer + timer_start(&(global_timers[TIMER_VIC_INIT])); + // read global parameters vic_cesm_start(vclock, cmeta); @@ -93,6 +99,11 @@ vic_cesm_init(vic_clock *vclock, // initialize output structures vic_init_output(&dmy_current); + // stop init timer + timer_stop(&(global_timers[TIMER_VIC_INIT])); + // stop vic all timer + timer_stop(&(global_timers[TIMER_VIC_ALL])); + return EXIT_SUCCESS; } @@ -102,6 +113,13 @@ vic_cesm_init(vic_clock *vclock, int vic_cesm_run(vic_clock *vclock) { + char state_filename[MAXSTRING]; + + // continue vic all timer + timer_continue(&(global_timers[TIMER_VIC_ALL])); + // start vic run timer + timer_start(&(global_timers[TIMER_VIC_RUN])); + // reset l2x fields initialize_l2x_data(); @@ -119,7 +137,8 @@ vic_cesm_run(vic_clock *vclock) // if save: if (vclock->state_flag) { - vic_store(&dmy_current); + vic_store(&dmy_current, state_filename); + write_rpointer_file(state_filename); } // reset x2l fields @@ -129,6 +148,11 @@ vic_cesm_run(vic_clock *vclock) advance_time(); assert_time_insync(vclock, &dmy_current); + // stop vic run timer + timer_stop(&(global_timers[TIMER_VIC_RUN])); + // stop vic all timer + timer_stop(&(global_timers[TIMER_VIC_ALL])); + return EXIT_SUCCESS; } @@ -138,8 +162,20 @@ vic_cesm_run(vic_clock *vclock) int vic_cesm_final() { + // continue vic all timer + timer_continue(&(global_timers[TIMER_VIC_ALL])); + // start vic run timer + timer_start(&(global_timers[TIMER_VIC_RUN])); + // clean up vic_cesm_finalize(); + // stop vic final timer + timer_stop(&(global_timers[TIMER_VIC_FINAL])); + // stop vic all timer + timer_stop(&(global_timers[TIMER_VIC_ALL])); + // write timing info + write_vic_timing_table(global_timers, VIC_DRIVER); + return EXIT_SUCCESS; } diff --git a/vic/drivers/cesm/src/cesm_put_data.c b/vic/drivers/cesm/src/cesm_put_data.c index a2696d47b..4e04a723a 100644 --- a/vic/drivers/cesm/src/cesm_put_data.c +++ b/vic/drivers/cesm/src/cesm_put_data.c @@ -33,7 +33,7 @@ void vic_cesm_put_data() { extern all_vars_struct *all_vars; - extern atmos_data_struct *atmos; + extern force_data_struct *force; extern dmy_struct dmy_current; extern domain_struct local_domain; extern soil_con_struct *soil_con; @@ -140,26 +140,26 @@ vic_cesm_put_data() // 2m reference temperature // CESM units: K - l2x_vic[i].l2x_Sl_tref += AreaFactor * atmos->air_temp[NR]; + l2x_vic[i].l2x_Sl_tref += AreaFactor * force->air_temp[NR]; // 2m reference specific humidity // CESM units: g/g l2x_vic[i].l2x_Sl_qref += AreaFactor * CONST_EPS * - atmos->vp[NR] / atmos->pressure[NR]; + force->vp[NR] / force->pressure[NR]; // Albedo Note: VIC does not partition its albedo, all returned // values will be the same // albedo: direct, visible // CESM units: unitless - // atmos->shortwave is the incoming shortwave (+ down) - // atmos->NetShortAtmos net shortwave flux (+ down) - // SWup = atmos->shortwave[NR] - energy.NetShortAtmos + // force->shortwave is the incoming shortwave (+ down) + // force->NetShortAtmos net shortwave flux (+ down) + // SWup = force->shortwave[NR] - energy.NetShortAtmos // Set the albedo to zero for the case where there is no shortwave down - if (atmos->shortwave[NR] > 0.) { + if (force->shortwave[NR] > 0.) { albedo = AreaFactor * - (atmos->shortwave[NR] - energy.NetShortAtmos) / - atmos->shortwave[NR]; + (force->shortwave[NR] - energy.NetShortAtmos) / + force->shortwave[NR]; } else { albedo = 0.; @@ -184,7 +184,7 @@ vic_cesm_put_data() // 10m wind // CESM units: m/s - l2x_vic[i].l2x_Sl_u10 += AreaFactor * atmos->wind[NR]; + l2x_vic[i].l2x_Sl_u10 += AreaFactor * force->wind[NR]; // dry deposition velocities (optional) // CESM units: ? @@ -230,13 +230,13 @@ vic_cesm_put_data() // wind stress, zonal // CESM units: N m-2 - wind_stress_x = -1 * atmos[i].density[NR] * + wind_stress_x = -1 * force[i].density[NR] * x2l_vic[i].x2l_Sa_u / aero_resist; l2x_vic[i].l2x_Fall_taux += AreaFactor * wind_stress_x; // wind stress, meridional // CESM units: N m-2 - wind_stress_y = -1 * atmos[i].density[NR] * + wind_stress_y = -1 * force[i].density[NR] * x2l_vic[i].x2l_Sa_v / aero_resist; l2x_vic[i].l2x_Fall_tauy += AreaFactor * wind_stress_y; @@ -245,7 +245,7 @@ vic_cesm_put_data() wind_stress = sqrt(pow(wind_stress_x, 2) + pow(wind_stress_y, 2)); l2x_vic[i].l2x_Sl_fv += AreaFactor * - (wind_stress / atmos[i].density[NR]); + (wind_stress / force[i].density[NR]); // latent heat flux // CESM units: W m-2 @@ -259,7 +259,7 @@ vic_cesm_put_data() // upward longwave heat flux // CESM units: W m-2 l2x_vic[i].l2x_Fall_lwup += AreaFactor * - (atmos->longwave[NR] - + (force->longwave[NR] - energy.NetLongAtmos); // evaporation water flux @@ -278,7 +278,7 @@ vic_cesm_put_data() // heat flux shortwave net l2x_vic[i].l2x_Fall_swnet += AreaFactor * - (atmos->shortwave[NR] - + (force->shortwave[NR] - energy.NetShortAtmos); // co2 flux **For testing set to 0 @@ -313,7 +313,7 @@ vic_cesm_put_data() } if (!assert_close_double(AreaFactorSum, 1., 0., 1e-3)) { - log_warn("AreaFactorSum (%f) is not 1 in cesm_put_data.c", + log_warn("AreaFactorSum (%f) is not 1", AreaFactorSum); } } diff --git a/vic/drivers/cesm/src/display_current_settings.c b/vic/drivers/cesm/src/display_current_settings.c index 27c1c540b..86351b418 100644 --- a/vic/drivers/cesm/src/display_current_settings.c +++ b/vic/drivers/cesm/src/display_current_settings.c @@ -266,8 +266,7 @@ display_current_settings(int mode) fprintf(LOG_DEST, "\n"); fprintf(LOG_DEST, "Constants File\t\t%s\n", filenames.constants); - fprintf(LOG_DEST, "Input Soil Data:\n"); - fprintf(LOG_DEST, "Soil file\t\t%s\n", filenames.soil); + fprintf(LOG_DEST, "Parameters file\t\t%s\n", filenames.params); if (options.BASEFLOW == ARNO) { fprintf(LOG_DEST, "BASEFLOW\t\tARNO\n"); } @@ -288,15 +287,12 @@ display_current_settings(int mode) } fprintf(LOG_DEST, "\n"); - fprintf(LOG_DEST, "Input Veg Data:\n"); - fprintf(LOG_DEST, "Veg library file\t%s\n", filenames.veglib); if (options.VEGLIB_PHOTO) { fprintf(LOG_DEST, "VEGLIB_PHOTO\t\tTRUE\n"); } else { fprintf(LOG_DEST, "VEGLIB_PHOTO\t\tFALSE\n"); } - fprintf(LOG_DEST, "Veg param file\t\t%s\n", filenames.veg); fprintf(LOG_DEST, "ROOT_ZONES\t\t%zu\n", options.ROOT_ZONES); if (options.VEGPARAM_LAI) { fprintf(LOG_DEST, "VEGPARAM_LAI\t\tTRUE\n"); @@ -350,7 +346,7 @@ display_current_settings(int mode) fprintf(LOG_DEST, "\n"); fprintf(LOG_DEST, "Input Lake Data:\n"); if (options.LAKES) { - fprintf(LOG_DEST, "LAKES\t\tTRUE\t%s\n", filenames.lakeparam); + fprintf(LOG_DEST, "LAKES\t\tTRUE\n"); } else { fprintf(LOG_DEST, "LAKES\t\tFALSE\n"); diff --git a/vic/drivers/cesm/src/get_global_param.c b/vic/drivers/cesm/src/get_global_param.c index b833144ac..7cc7c7459 100644 --- a/vic/drivers/cesm/src/get_global_param.c +++ b/vic/drivers/cesm/src/get_global_param.c @@ -240,6 +240,16 @@ get_global_param(FILE *gp) else if (strcasecmp("LOG_DIR", optstr) == 0) { sscanf(cmdstr, "%*s %s", filenames.log_path); } + else if (strcasecmp("INIT_STATE", optstr) == 0) { + sscanf(cmdstr, "%*s %s", flgstr); + if (strcasecmp("FALSE", flgstr) == 0) { + options.INIT_STATE = false; + } + else { + options.INIT_STATE = true; + strcpy(filenames.init_state, flgstr); + } + } // Define state file format else if (strcasecmp("STATE_FORMAT", optstr) == 0) { sscanf(cmdstr, "%*s %s", flgstr); @@ -274,8 +284,8 @@ get_global_param(FILE *gp) else if (strcasecmp("DOMAIN_TYPE", optstr) == 0) { get_domain_type(cmdstr); } - else if (strcasecmp("SOIL", optstr) == 0) { - sscanf(cmdstr, "%*s %s", filenames.soil); + else if (strcasecmp("PARAMETERS", optstr) == 0) { + sscanf(cmdstr, "%*s %s", filenames.params); } else if (strcasecmp("ARNO_PARAMS", optstr) == 0) { sscanf(cmdstr, "%*s %s", flgstr); @@ -320,16 +330,10 @@ get_global_param(FILE *gp) sscanf(cmdstr, "%*s %s", flgstr); options.ORGANIC_FRACT = str_to_bool(flgstr); } - else if (strcasecmp("VEGLIB", optstr) == 0) { - sscanf(cmdstr, "%*s %s", filenames.veglib); - } else if (strcasecmp("VEGLIB_PHOTO", optstr) == 0) { sscanf(cmdstr, "%*s %s", flgstr); options.VEGLIB_PHOTO = str_to_bool(flgstr); } - else if (strcasecmp("VEGPARAM", optstr) == 0) { - sscanf(cmdstr, "%*s %s", filenames.veg); - } else if (strcasecmp("LAI_SRC", optstr) == 0) { sscanf(cmdstr, "%*s %s", flgstr); if (strcasecmp("FROM_VEGHIST", flgstr) == 0) { @@ -388,7 +392,6 @@ get_global_param(FILE *gp) } else { options.LAKES = true; - strcpy(filenames.lakeparam, flgstr); } } else if (strcasecmp("LAKE_PROFILE", optstr) == 0) { @@ -458,7 +461,7 @@ validate_filenames(filenames_struct *filenames) extern option_struct options; // Validate log directory - if (strcmp(filenames->result_dir, "MISSING") == 0) { + if (strcmp(filenames->log_path, "MISSING") == 0) { log_err("Log directory must be specified in CESM driver"); } @@ -469,23 +472,11 @@ validate_filenames(filenames_struct *filenames) "begins with \"RESULT_DIR\"."); } - // Validate soil parameter file information - if (strcmp(filenames->soil, "MISSING") == 0) { - log_err("No soil parameter file has been defined. Make sure that the " - "global file defines the soil parameter file on the line that " - "begins with \"SOIL\"."); - } - - // Validate veg parameter information - if (strcmp(filenames->veg, "MISSING") == 0) { - log_err("No vegetation parameter file has been defined. Make sure " - "that the global file defines the vegetation parameter " - "file on the line that begins with \"VEGPARAM\"."); - } - if (strcmp(filenames->veglib, "MISSING") == 0) { - log_err("No vegetation library file has been defined. Make sure " - "that the global file defines the vegetation library file " - "on the line that begins with \"VEGLIB\"."); + // Validate parameter file information + if (strcmp(filenames->params, "MISSING") == 0) { + log_err("A parameters file has not been defined. Make sure that the " + "global file defines the parameters parameter file on the line " + "that begins with \"PARAMETERS\"."); } // Validate snow parameter file diff --git a/vic/drivers/cesm/src/vic_force.c b/vic/drivers/cesm/src/vic_force.c index 1811c1caa..252bb5bbd 100644 --- a/vic/drivers/cesm/src/vic_force.c +++ b/vic/drivers/cesm/src/vic_force.c @@ -1,7 +1,7 @@ /****************************************************************************** * @section DESCRIPTION * - * Read atmospheric forcing data. + * Unpack forcing data. * * @section LICENSE * @@ -35,7 +35,7 @@ vic_force(void) extern size_t NF; extern size_t NR; extern size_t current; - extern atmos_data_struct *atmos; + extern force_data_struct *force; extern x2l_data_struct *x2l_vic; extern dmy_struct dmy_current; extern domain_struct global_domain; @@ -72,7 +72,7 @@ vic_force(void) for (i = 0; i < local_domain.ncells_active; i++) { // CESM units: K // VIC units: C - atmos[i].air_temp[j] = x2l_vic[i].x2l_Sa_tbot - CONST_TKFRZ; + force[i].air_temp[j] = x2l_vic[i].x2l_Sa_tbot - CONST_TKFRZ; } } @@ -82,7 +82,7 @@ vic_force(void) // CESM units: km m-2 s-1 // VIC units: mm / timestep // Note: VIC does not use liquid/solid precip partitioning - atmos[i].prec[j] = (x2l_vic[i].x2l_Faxa_rainc + + force[i].prec[j] = (x2l_vic[i].x2l_Faxa_rainc + x2l_vic[i].x2l_Faxa_rainl + x2l_vic[i].x2l_Faxa_snowc + x2l_vic[i].x2l_Faxa_snowl) * @@ -96,7 +96,7 @@ vic_force(void) // CESM units: W m-2 // VIC units: W m-2 // Note: VIC does not use partitioned shortwave fluxes. - atmos[i].shortwave[j] = (x2l_vic[i].x2l_Faxa_swndr + + force[i].shortwave[j] = (x2l_vic[i].x2l_Faxa_swndr + x2l_vic[i].x2l_Faxa_swvdr + x2l_vic[i].x2l_Faxa_swndf + x2l_vic[i].x2l_Faxa_swvdf); @@ -108,7 +108,7 @@ vic_force(void) for (i = 0; i < local_domain.ncells_active; i++) { // CESM units: W m-2 // VIC units: W m-2 - atmos[i].longwave[j] = x2l_vic[i].x2l_Faxa_lwdn; + force[i].longwave[j] = x2l_vic[i].x2l_Faxa_lwdn; } } @@ -118,7 +118,7 @@ vic_force(void) // CESM units: m s-1 // VIC units: m s-1 // Note: VIC does not use partitioned wind speeds - atmos[i].wind[j] = sqrt(pow(x2l_vic[i].x2l_Sa_u, 2) + + force[i].wind[j] = sqrt(pow(x2l_vic[i].x2l_Sa_u, 2) + pow(x2l_vic[i].x2l_Sa_v, 2)); } } @@ -128,7 +128,7 @@ vic_force(void) for (i = 0; i < local_domain.ncells_active; i++) { // CESM units: Pa // VIC units: kPa - atmos[i].pressure[j] = x2l_vic[i].x2l_Sa_pbot / PA_PER_KPA; + force[i].pressure[j] = x2l_vic[i].x2l_Sa_pbot / PA_PER_KPA; } } @@ -137,8 +137,8 @@ vic_force(void) for (i = 0; i < local_domain.ncells_active; i++) { // CESM units: shum is specific humidity (g/g) // VIC units: kPa - atmos[i].vp[j] = q_to_vp(x2l_vic[i].x2l_Sa_shum, - atmos[i].pressure[j]); + force[i].vp[j] = q_to_vp(x2l_vic[i].x2l_Sa_shum, + force[i].pressure[j]); } } @@ -148,14 +148,14 @@ vic_force(void) for (i = 0; i < local_domain.ncells_active; i++) { // CESM units: n/a (calculated from SW fluxes) // VIC units: fraction - if (atmos[i].shortwave[j] != 0.) { - atmos[i].fdir[j] = (x2l_vic[i].x2l_Faxa_swndr + + if (force[i].shortwave[j] != 0.) { + force[i].fdir[j] = (x2l_vic[i].x2l_Faxa_swndr + x2l_vic[i].x2l_Faxa_swvdr) / (x2l_vic[i].x2l_Faxa_swndf + x2l_vic[i].x2l_Faxa_swvdf); } else { - atmos[i].fdir[j] = 0.; + force[i].fdir[j] = 0.; } } } @@ -165,14 +165,14 @@ vic_force(void) for (i = 0; i < local_domain.ncells_active; i++) { // CESM units: 1e-6 mol/mol // VIC units: mol CO2/ mol air - atmos[i].Catm[j] = 1e6 * x2l_vic[i].x2l_Sa_co2prog; + force[i].Catm[j] = 1e6 * x2l_vic[i].x2l_Sa_co2prog; } } // Cosine of solar zenith angle for (j = 0; j < NF; j++) { for (i = 0; i < local_domain.ncells_active; i++) { - atmos[i].coszen[j] = compute_coszen( + force[i].coszen[j] = compute_coszen( local_domain.locations[i].latitude, local_domain.locations[i].longitude, soil_con[i].time_zone_lng, dmy_current.day_in_year, @@ -187,14 +187,14 @@ vic_force(void) for (i = 0; i < local_domain.ncells_active; i++) { // CESM units: kg m-2 s-1 // VIC units: mm - atmos[i].channel_in[j] = x2l_vic[i].x2l_Flrr_flood * + force[i].channel_in[j] = x2l_vic[i].x2l_Flrr_flood * global_param.snow_dt; } } } if (options.SNOW_BAND > 1) { - log_err("SNOW_BAND not implemented in vic_force()"); + log_err("SNOW_BAND not implemented"); } else { t_offset = 0; @@ -204,49 +204,49 @@ vic_force(void) for (i = 0; i < local_domain.ncells_active; i++) { for (j = 0; j < NF; j++) { // vapor pressure deficit - atmos[i].vpd[j] = svp(atmos[i].air_temp[j]) - atmos[i].vp[j]; + force[i].vpd[j] = svp(force[i].air_temp[j]) - force[i].vp[j]; // photosynthetically active radiation // TODO: Add CARBON_SW2PAR back to the parameters structure - // atmos[i].par[j] = param.CARBON_SW2PAR * atmos[i].shortwave[j]; + // force[i].par[j] = param.CARBON_SW2PAR * force[i].shortwave[j]; // air density - atmos[i].density[j] = air_density(atmos[i].air_temp[j], - atmos[i].pressure[j]); + force[i].density[j] = air_density(force[i].air_temp[j], + force[i].pressure[j]); // snow flag - atmos[i].snowflag[j] = will_it_snow(&(atmos[i].air_temp[j]), + force[i].snowflag[j] = will_it_snow(&(force[i].air_temp[j]), t_offset, param.SNOW_MAX_SNOW_TEMP, - &(atmos[i].prec[j]), 1); + &(force[i].prec[j]), 1); } } // Put average value in NR field for (i = 0; i < local_domain.ncells_active; i++) { - atmos[i].air_temp[NR] = average(atmos[i].air_temp, NF); + force[i].air_temp[NR] = average(force[i].air_temp, NF); // For precipitation put total - atmos[i].prec[NR] = average(atmos[i].prec, NF) * NF; - atmos[i].shortwave[NR] = average(atmos[i].shortwave, NF); - atmos[i].longwave[NR] = average(atmos[i].longwave, NF); - atmos[i].pressure[NR] = average(atmos[i].pressure, NF); - atmos[i].wind[NR] = average(atmos[i].wind, NF); - atmos[i].vp[NR] = average(atmos[i].vp, NF); - atmos[i].vpd[NR] = (svp(atmos[i].air_temp[NR]) - atmos[i].vp[NR]); - atmos[i].density[NR] = air_density(atmos[i].air_temp[NR], - atmos[i].pressure[NR]); - atmos[i].snowflag[NR] = will_it_snow(atmos[i].air_temp, t_offset, + force[i].prec[NR] = average(force[i].prec, NF) * NF; + force[i].shortwave[NR] = average(force[i].shortwave, NF); + force[i].longwave[NR] = average(force[i].longwave, NF); + force[i].pressure[NR] = average(force[i].pressure, NF); + force[i].wind[NR] = average(force[i].wind, NF); + force[i].vp[NR] = average(force[i].vp, NF); + force[i].vpd[NR] = (svp(force[i].air_temp[NR]) - force[i].vp[NR]); + force[i].density[NR] = air_density(force[i].air_temp[NR], + force[i].pressure[NR]); + force[i].snowflag[NR] = will_it_snow(force[i].air_temp, t_offset, param.SNOW_MAX_SNOW_TEMP, - atmos[i].prec, NF); + force[i].prec, NF); // Optional inputs if (options.LAKES) { - atmos[i].channel_in[NR] = average(atmos[i].channel_in, NF) * NF; + force[i].channel_in[NR] = average(force[i].channel_in, NF) * NF; } if (options.CARBON) { - atmos[i].Catm[NR] = average(atmos[i].Catm, NF); - atmos[i].fdir[NR] = average(atmos[i].fdir, NF); - atmos[i].par[NR] = average(atmos[i].par, NF); + force[i].Catm[NR] = average(force[i].Catm, NF); + force[i].fdir[NR] = average(force[i].fdir, NF); + force[i].par[NR] = average(force[i].par, NF); // for coszen, use value at noon - atmos[i].coszen[NR] = compute_coszen( + force[i].coszen[NR] = compute_coszen( local_domain.locations[i].latitude, local_domain.locations[i].longitude, soil_con[i].time_zone_lng, dmy_current.day_in_year, SEC_PER_DAY / 2); diff --git a/vic/drivers/cesm/src/vic_populate_model_state.c b/vic/drivers/cesm/src/vic_populate_model_state.c index caba92974..4281d034e 100644 --- a/vic/drivers/cesm/src/vic_populate_model_state.c +++ b/vic/drivers/cesm/src/vic_populate_model_state.c @@ -48,21 +48,32 @@ vic_populate_model_state(char *runtype_str) runtype = start_type_from_char(runtype_str); - // read the model state from the netcdf file if there is one + // read the model state from the netcdf file if (runtype == CESM_RUNTYPE_RESTART || runtype == CESM_RUNTYPE_BRANCH) { // Get restart file from rpointer file read_rpointer_file(filenames.init_state); - // read initial state file + // set options.INIT_STATE to true since we have found a state file in + // the rpointer file. + options.INIT_STATE = true; + + // read initial state file -- specified in rpointer file vic_restore(); } else if (runtype == CESM_RUNTYPE_CLEANSTART) { - // run type is clean start - for (i = 0; i < local_domain.ncells_active; i++) { - generate_default_state(&(all_vars[i]), &(soil_con[i]), veg_con[i]); - if (options.LAKES) { - generate_default_lake_state(&(all_vars[i]), &(soil_con[i]), - lake_con[i]); + if (options.INIT_STATE) { + // read initial state file -- specified in global param file + vic_restore(); + } + else { + // no initial state file specified - generate default state + for (i = 0; i < local_domain.ncells_active; i++) { + generate_default_state(&(all_vars[i]), &(soil_con[i]), + veg_con[i]); + if (options.LAKES) { + generate_default_lake_state(&(all_vars[i]), &(soil_con[i]), + lake_con[i]); + } } } } @@ -105,20 +116,39 @@ void read_rpointer_file(char *fname) { FILE *fp = NULL; + char linestr[MAXSTRING]; - fname = NULL; + fp = open_file(RPOINTER, "r"); - fp = fopen(RPOINTER, "r"); + fgets(linestr, MAXSTRING, fp); - if (fp == NULL) { - log_err("Error reading rpointer file %s", RPOINTER); + // Read through rpointer file file to find state file name + while (!feof(fp)) { + if (linestr[0] != '#' && linestr[0] != '\n' && linestr[0] != '\0') { + sscanf(linestr, "%s", fname); + break; + } + fgets(linestr, MAXSTRING, fp); } + fclose(fp); - fgets(fname, MAXSTRING, fp); + // remove trailing newline if present + fname[strcspn(fname, "\n")] = 0; +} - fclose(fp); +/****************************************************************************** + * @brief Write rpointer file + *****************************************************************************/ +void +write_rpointer_file(char *fname) +{ + FILE *fp = NULL; + char *header = "# VIC CESM Driver restart pointer file\n"; - if (fname == NULL) { - log_err("Error reading rpointer file %s", RPOINTER); - } + fp = open_file(RPOINTER, "w"); + + fprintf(fp, header, fname); + fprintf(fp, "%s\n", fname); + + fclose(fp); } diff --git a/vic/drivers/classic/include/vic_driver_classic.h b/vic/drivers/classic/include/vic_driver_classic.h index 5a21d311d..d7365f6bf 100644 --- a/vic/drivers/classic/include/vic_driver_classic.h +++ b/vic/drivers/classic/include/vic_driver_classic.h @@ -35,7 +35,43 @@ #define MAX_VEGPARAM_LINE_LENGTH 500 #define ASCII_STATE_FLOAT_FMT "%.16g" -void alloc_atmos(int, atmos_data_struct **); +/****************************************************************************** + * @brief file structures + *****************************************************************************/ +typedef struct { + FILE *forcing[MAX_FORCE_FILES]; /**< atmospheric forcing data files */ + FILE *globalparam; /**< global parameters file */ + FILE *constants; /**< model constants parameter file */ + FILE *init_state; /**< initial model state file */ + FILE *lakeparam; /**< lake parameter file */ + FILE *snowband; /**< snow elevation band data file */ + FILE *soilparam; /**< soil parameters for all grid cells */ + FILE *statefile; /**< output model state file */ + FILE *veglib; /**< vegetation parameters for all vege types */ + FILE *vegparam; /**< fractional coverage info for grid cell */ + FILE *logfile; /**< log file */ +} filep_struct; + +/****************************************************************************** + * @brief This structure stores input and output filenames. + *****************************************************************************/ +typedef struct { + char forcing[MAX_FORCE_FILES][MAXSTRING]; /**< atmospheric forcing data file names */ + char f_path_pfx[MAX_FORCE_FILES][MAXSTRING]; /**< path and prefix for atmospheric forcing data file names */ + char global[MAXSTRING]; /**< global control file name */ + char constants[MAXSTRING]; /**< model constants file name */ + char init_state[MAXSTRING]; /**< initial model state file name */ + char lakeparam[MAXSTRING]; /**< lake model constants file */ + char result_dir[MAXSTRING]; /**< directory where results will be written */ + char snowband[MAXSTRING]; /**< snow band parameter file name */ + char soil[MAXSTRING]; /**< soil parameter file name */ + char statefile[MAXSTRING]; /**< name of file in which to store model state */ + char veg[MAXSTRING]; /**< vegetation grid coverage file */ + char veglib[MAXSTRING]; /**< vegetation parameter library file */ + char log_path[MAXSTRING]; /**< Location to write log file to*/ +} filenames_struct; + +void alloc_atmos(int, force_data_struct **); void alloc_veg_hist(int nrecs, int nveg, veg_hist_struct ***veg_hist); void calc_netlongwave(double *, double, double, double); double calc_netshort(double, int, double, double *); @@ -44,18 +80,20 @@ bool check_save_state_flag(dmy_struct *, size_t); FILE *check_state_file(char *, size_t, size_t, int *); void close_files(filep_struct *filep, stream_struct **streams); void compute_cell_area(soil_con_struct *); -void free_atmos(int nrecs, atmos_data_struct **atmos); +void free_atmos(int nrecs, force_data_struct **force); void free_veg_hist(int nrecs, int nveg, veg_hist_struct ***veg_hist); void free_veglib(veg_lib_struct **); double get_dist(double lat1, double long1, double lat2, double long2); void get_force_type(char *, int, int *); void get_global_param(FILE *); +void initialize_filenames(void); +void initialize_fileps(void); void initialize_forcing_files(void); void make_in_and_outfiles(filep_struct *filep, filenames_struct *filenames, soil_con_struct *soil, stream_struct **streams, dmy_struct *dmy); FILE *open_state_file(global_param_struct *, filenames_struct, size_t, size_t); -void print_atmos_data(atmos_data_struct *atmos, size_t nr); +void print_atmos_data(force_data_struct *force, size_t nr); void parse_output_info(FILE *gp, stream_struct **output_streams, dmy_struct *dmy_current); void read_atmos_data(FILE *, global_param_struct, int, int, double **, @@ -65,10 +103,11 @@ void read_initial_model_state(FILE *, all_vars_struct *, int, int, int, soil_con_struct *, lake_con_struct); lake_con_struct read_lakeparam(FILE *, soil_con_struct, veg_con_struct *); void read_snowband(FILE *, soil_con_struct *); -soil_con_struct read_soilparam(FILE *, char *, char *); +void read_soilparam(FILE *soilparam, soil_con_struct *temp, bool *RUN_MODEL, + bool *MODEL_DONE); veg_lib_struct *read_veglib(FILE *, size_t *); veg_con_struct *read_vegparam(FILE *, int, size_t); -void vic_force(atmos_data_struct *, dmy_struct *, FILE **, veg_con_struct *, +void vic_force(force_data_struct *, dmy_struct *, FILE **, veg_con_struct *, veg_hist_struct **, soil_con_struct *); void vic_populate_model_state(all_vars_struct *, filep_struct, size_t, soil_con_struct *, veg_con_struct *, @@ -78,4 +117,5 @@ void write_header(stream_struct **streams, dmy_struct *dmy); void write_model_state(all_vars_struct *, int, int, filep_struct *, soil_con_struct *); void write_output(stream_struct **streams, dmy_struct *dmy); +void write_vic_timing_table(timer_struct *timers); #endif diff --git a/vic/drivers/classic/src/alloc_atmos.c b/vic/drivers/classic/src/alloc_atmos.c index 13a2bbd55..c41584040 100644 --- a/vic/drivers/classic/src/alloc_atmos.c +++ b/vic/drivers/classic/src/alloc_atmos.c @@ -31,49 +31,50 @@ *****************************************************************************/ void alloc_atmos(int nrecs, - atmos_data_struct **atmos) + force_data_struct **force) { extern option_struct options; int i; - *atmos = calloc(nrecs, sizeof(atmos_data_struct)); - check_alloc_status(*atmos, "Memory allocation error."); + *force = calloc(nrecs, sizeof(force_data_struct)); + check_alloc_status(*force, "Memory allocation error."); for (i = 0; i < nrecs; i++) { - (*atmos)[i].air_temp = calloc(NR + 1, sizeof(*(*atmos)[i].air_temp)); - check_alloc_status((*atmos)[i].air_temp, "Memory allocation error."); - (*atmos)[i].density = calloc(NR + 1, sizeof(*(*atmos)[i].density)); - check_alloc_status((*atmos)[i].density, "Memory allocation error."); - (*atmos)[i].longwave = calloc(NR + 1, sizeof(*(*atmos)[i].longwave)); - check_alloc_status((*atmos)[i].longwave, "Memory allocation error."); - (*atmos)[i].prec = calloc(NR + 1, sizeof(*(*atmos)[i].prec)); - check_alloc_status((*atmos)[i].prec, "Memory allocation error."); - (*atmos)[i].pressure = calloc(NR + 1, sizeof(*(*atmos)[i].pressure)); - check_alloc_status((*atmos)[i].pressure, "Memory allocation error."); - (*atmos)[i].shortwave = calloc(NR + 1, sizeof(*(*atmos)[i].shortwave)); - check_alloc_status((*atmos)[i].shortwave, "Memory allocation error."); - (*atmos)[i].snowflag = calloc(NR + 1, sizeof(*(*atmos)[i].snowflag)); - check_alloc_status((*atmos)[i].snowflag, "Memory allocation error."); - (*atmos)[i].vp = calloc(NR + 1, sizeof(*(*atmos)[i].vp)); - check_alloc_status((*atmos)[i].vp, "Memory allocation error."); - (*atmos)[i].vpd = calloc(NR + 1, sizeof(*(*atmos)[i].vpd)); - check_alloc_status((*atmos)[i].vpd, "Memory allocation error."); - (*atmos)[i].wind = calloc(NR + 1, sizeof(*(*atmos)[i].wind)); - check_alloc_status((*atmos)[i].wind, "Memory allocation error."); + (*force)[i].air_temp = calloc(NR + 1, sizeof(*(*force)[i].air_temp)); + check_alloc_status((*force)[i].air_temp, "Memory allocation error."); + (*force)[i].density = calloc(NR + 1, sizeof(*(*force)[i].density)); + check_alloc_status((*force)[i].density, "Memory allocation error."); + (*force)[i].longwave = calloc(NR + 1, sizeof(*(*force)[i].longwave)); + check_alloc_status((*force)[i].longwave, "Memory allocation error."); + (*force)[i].prec = calloc(NR + 1, sizeof(*(*force)[i].prec)); + check_alloc_status((*force)[i].prec, "Memory allocation error."); + (*force)[i].pressure = calloc(NR + 1, sizeof(*(*force)[i].pressure)); + check_alloc_status((*force)[i].pressure, "Memory allocation error."); + (*force)[i].shortwave = calloc(NR + 1, sizeof(*(*force)[i].shortwave)); + check_alloc_status((*force)[i].shortwave, "Memory allocation error."); + (*force)[i].snowflag = calloc(NR + 1, sizeof(*(*force)[i].snowflag)); + check_alloc_status((*force)[i].snowflag, "Memory allocation error."); + (*force)[i].vp = calloc(NR + 1, sizeof(*(*force)[i].vp)); + check_alloc_status((*force)[i].vp, "Memory allocation error."); + (*force)[i].vpd = calloc(NR + 1, sizeof(*(*force)[i].vpd)); + check_alloc_status((*force)[i].vpd, "Memory allocation error."); + (*force)[i].wind = calloc(NR + 1, sizeof(*(*force)[i].wind)); + check_alloc_status((*force)[i].wind, "Memory allocation error."); if (options.LAKES) { - (*atmos)[i].channel_in = - calloc(NR + 1, sizeof(*(*atmos)[i].channel_in)); - check_alloc_status((*atmos)[i].channel_in, "Memory allocation error."); + (*force)[i].channel_in = + calloc(NR + 1, sizeof(*(*force)[i].channel_in)); + check_alloc_status((*force)[i].channel_in, + "Memory allocation error."); } if (options.CARBON) { - (*atmos)[i].Catm = calloc(NR + 1, sizeof(*(*atmos)[i].Catm)); - check_alloc_status((*atmos)[i].Catm, "Memory allocation error."); - (*atmos)[i].coszen = calloc(NR + 1, sizeof(*(*atmos)[i].coszen)); - check_alloc_status((*atmos)[i].coszen, "Memory allocation error."); - (*atmos)[i].fdir = calloc(NR + 1, sizeof(*(*atmos)[i].fdir)); - check_alloc_status((*atmos)[i].fdir, "Memory allocation error."); - (*atmos)[i].par = calloc(NR + 1, sizeof(*(*atmos)[i].par)); - check_alloc_status((*atmos)[i].par, "Memory allocation error."); + (*force)[i].Catm = calloc(NR + 1, sizeof(*(*force)[i].Catm)); + check_alloc_status((*force)[i].Catm, "Memory allocation error."); + (*force)[i].coszen = calloc(NR + 1, sizeof(*(*force)[i].coszen)); + check_alloc_status((*force)[i].coszen, "Memory allocation error."); + (*force)[i].fdir = calloc(NR + 1, sizeof(*(*force)[i].fdir)); + check_alloc_status((*force)[i].fdir, "Memory allocation error."); + (*force)[i].par = calloc(NR + 1, sizeof(*(*force)[i].par)); + check_alloc_status((*force)[i].par, "Memory allocation error."); } } } @@ -83,36 +84,36 @@ alloc_atmos(int nrecs, *****************************************************************************/ void free_atmos(int nrecs, - atmos_data_struct **atmos) + force_data_struct **force) { extern option_struct options; int i; - if (*atmos == NULL) { + if (*force == NULL) { return; } for (i = 0; i < nrecs; i++) { - free((*atmos)[i].air_temp); - free((*atmos)[i].density); - free((*atmos)[i].longwave); - free((*atmos)[i].prec); - free((*atmos)[i].pressure); - free((*atmos)[i].shortwave); - free((*atmos)[i].snowflag); - free((*atmos)[i].vp); - free((*atmos)[i].vpd); - free((*atmos)[i].wind); + free((*force)[i].air_temp); + free((*force)[i].density); + free((*force)[i].longwave); + free((*force)[i].prec); + free((*force)[i].pressure); + free((*force)[i].shortwave); + free((*force)[i].snowflag); + free((*force)[i].vp); + free((*force)[i].vpd); + free((*force)[i].wind); if (options.LAKES) { - free((*atmos)[i].channel_in); + free((*force)[i].channel_in); } if (options.CARBON) { - free((*atmos)[i].Catm); - free((*atmos)[i].coszen); - free((*atmos)[i].fdir); - free((*atmos)[i].par); + free((*force)[i].Catm); + free((*force)[i].coszen); + free((*force)[i].fdir); + free((*force)[i].par); } } - free(*atmos); + free(*force); } diff --git a/vic/drivers/classic/src/alloc_veg_hist.c b/vic/drivers/classic/src/alloc_veg_hist.c index d479eea79..840e360ce 100644 --- a/vic/drivers/classic/src/alloc_veg_hist.c +++ b/vic/drivers/classic/src/alloc_veg_hist.c @@ -46,22 +46,27 @@ alloc_veg_hist(int nrecs, for (j = 0; j < nveg + 1; j++) { (*veg_hist)[i][j].albedo = calloc(NR + 1, sizeof(*((*veg_hist)[i][j].albedo))); - check_alloc_status((*veg_hist)[i][j].albedo, "Memory allocation error."); + check_alloc_status((*veg_hist)[i][j].albedo, + "Memory allocation error."); (*veg_hist)[i][j].displacement = calloc(NR + 1, sizeof(*((*veg_hist)[i][j]. displacement))); - check_alloc_status((*veg_hist)[i][j].displacement, "Memory allocation error."); + check_alloc_status((*veg_hist)[i][j].displacement, + "Memory allocation error."); (*veg_hist)[i][j].fcanopy = calloc(NR + 1, sizeof(*((*veg_hist)[i][j]. fcanopy))); - check_alloc_status((*veg_hist)[i][j].fcanopy, "Memory allocation error."); + check_alloc_status((*veg_hist)[i][j].fcanopy, + "Memory allocation error."); (*veg_hist)[i][j].LAI = calloc(NR + 1, sizeof(*((*veg_hist)[i][j].LAI))); - check_alloc_status((*veg_hist)[i][j].LAI, "Memory allocation error."); + check_alloc_status((*veg_hist)[i][j].LAI, + "Memory allocation error."); (*veg_hist)[i][j].roughness = calloc(NR + 1, sizeof(*((*veg_hist)[i][j]. roughness))); - check_alloc_status((*veg_hist)[i][j].roughness, "Memory allocation error."); + check_alloc_status((*veg_hist)[i][j].roughness, + "Memory allocation error."); } } } diff --git a/vic/drivers/shared_all/src/initialize_files.c b/vic/drivers/classic/src/initialize_files.c similarity index 97% rename from vic/drivers/shared_all/src/initialize_files.c rename to vic/drivers/classic/src/initialize_files.c index 1c4699c80..15935fb4f 100644 --- a/vic/drivers/shared_all/src/initialize_files.c +++ b/vic/drivers/classic/src/initialize_files.c @@ -25,7 +25,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *****************************************************************************/ -#include +#include /****************************************************************************** * @brief Initialize all filenames before they are called by the @@ -65,7 +65,6 @@ initialize_fileps() filep.globalparam = NULL; filep.constants = NULL; - filep.domain = NULL; filep.init_state = NULL; filep.lakeparam = NULL; filep.snowband = NULL; diff --git a/vic/drivers/classic/src/parse_output_info.c b/vic/drivers/classic/src/parse_output_info.c index bb9c1b6dc..a784df015 100644 --- a/vic/drivers/classic/src/parse_output_info.c +++ b/vic/drivers/classic/src/parse_output_info.c @@ -35,30 +35,29 @@ parse_output_info(FILE *gp, stream_struct **streams, dmy_struct *dmy_current) { - extern option_struct options; - extern global_param_struct global_param; - - char cmdstr[MAXSTRING]; - char optstr[MAXSTRING]; - char flgstr[MAXSTRING]; - short int streamnum; - char varname[MAXSTRING]; - int outvarnum; - char freq_type_str[MAXSTRING]; - char freq_value_str[MAXSTRING]; - char format[MAXSTRING]; - char typestr[MAXSTRING]; - int type; - char multstr[MAXSTRING]; - char aggstr[MAXSTRING]; - double mult; - unsigned short int freq; - int freq_n; - dmy_struct freq_dmy; - unsigned short int agg_type; - int found; - size_t nstream_vars[MAX_OUTPUT_STREAMS]; - bool default_outputs = false; + extern option_struct options; + + char cmdstr[MAXSTRING]; + char optstr[MAXSTRING]; + char flgstr[MAXSTRING]; + short int streamnum; + char varname[MAXSTRING]; + int outvarnum; + char freq_type_str[MAXSTRING]; + char freq_value_str[MAXSTRING]; + char format[MAXSTRING]; + char typestr[MAXSTRING]; + int type; + char multstr[MAXSTRING]; + char aggstr[MAXSTRING]; + double mult; + unsigned short int freq; + int freq_n; + dmy_struct freq_dmy; + unsigned short int agg_type; + int found; + size_t nstream_vars[MAX_OUTPUT_STREAMS]; + bool default_outputs = false; /** Read through global control file to find output info **/ @@ -195,6 +194,7 @@ parse_output_info(FILE *gp, strcpy(format, ""); strcpy(typestr, ""); strcpy(multstr, ""); + strcpy(aggstr, ""); found = sscanf(cmdstr, "%*s %s %s %s %s %s", varname, format, typestr, multstr, aggstr); if (!found) { diff --git a/vic/drivers/classic/src/print_library_classic.c b/vic/drivers/classic/src/print_library_classic.c index c90b0529c..d53fdfe33 100644 --- a/vic/drivers/classic/src/print_library_classic.c +++ b/vic/drivers/classic/src/print_library_classic.c @@ -30,7 +30,7 @@ * @brief Print atmos data structure. *****************************************************************************/ void -print_atmos_data(atmos_data_struct *atmos, +print_atmos_data(force_data_struct *force, size_t nr) { extern option_struct options; @@ -39,91 +39,136 @@ print_atmos_data(atmos_data_struct *atmos, fprintf(LOG_DEST, "atmos_data :\n"); fprintf(LOG_DEST, "\tair_temp :"); for (i = 0; i <= nr; i++) { - fprintf(LOG_DEST, "\t%.4f", atmos->air_temp[i]); + fprintf(LOG_DEST, "\t%.4f", force->air_temp[i]); } fprintf(LOG_DEST, "\n"); fprintf(LOG_DEST, "\tdensity :"); for (i = 0; i <= nr; i++) { - fprintf(LOG_DEST, "\t%.4f", atmos->density[i]); + fprintf(LOG_DEST, "\t%.4f", force->density[i]); } fprintf(LOG_DEST, "\n"); fprintf(LOG_DEST, "\tlongwave :"); for (i = 0; i <= nr; i++) { - fprintf(LOG_DEST, "\t%.4f", atmos->longwave[i]); + fprintf(LOG_DEST, "\t%.4f", force->longwave[i]); } fprintf(LOG_DEST, "\n"); fprintf(LOG_DEST, "\tout_prec :"); for (i = 0; i <= nr; i++) { - fprintf(LOG_DEST, "\t%.4f", atmos->out_prec); + fprintf(LOG_DEST, "\t%.4f", force->out_prec); } fprintf(LOG_DEST, "\n"); fprintf(LOG_DEST, "\tout_rain :"); for (i = 0; i <= nr; i++) { - fprintf(LOG_DEST, "\t%.4f", atmos->out_rain); + fprintf(LOG_DEST, "\t%.4f", force->out_rain); } fprintf(LOG_DEST, "\n"); fprintf(LOG_DEST, "\tout_snow :"); for (i = 0; i <= nr; i++) { - fprintf(LOG_DEST, "\t%.4f", atmos->out_snow); + fprintf(LOG_DEST, "\t%.4f", force->out_snow); } fprintf(LOG_DEST, "\n"); fprintf(LOG_DEST, "\tprec :"); for (i = 0; i <= nr; i++) { - fprintf(LOG_DEST, "\t%.4f", atmos->prec[i]); + fprintf(LOG_DEST, "\t%.4f", force->prec[i]); } fprintf(LOG_DEST, "\n"); fprintf(LOG_DEST, "\tpressure :"); for (i = 0; i <= nr; i++) { - fprintf(LOG_DEST, "\t%.4f", atmos->pressure[i]); + fprintf(LOG_DEST, "\t%.4f", force->pressure[i]); } fprintf(LOG_DEST, "\n"); fprintf(LOG_DEST, "\tshortwave :"); for (i = 0; i <= nr; i++) { - fprintf(LOG_DEST, "\t%.4f", atmos->shortwave[i]); + fprintf(LOG_DEST, "\t%.4f", force->shortwave[i]); } fprintf(LOG_DEST, "\n"); fprintf(LOG_DEST, "\tsnowflag :"); for (i = 0; i <= nr; i++) { - fprintf(LOG_DEST, "\t%d\n", atmos->snowflag[i]); + fprintf(LOG_DEST, "\t%d\n", force->snowflag[i]); } fprintf(LOG_DEST, "\n"); fprintf(LOG_DEST, "\tvp :"); for (i = 0; i <= nr; i++) { - fprintf(LOG_DEST, "\t%.4f", atmos->vp[i]); + fprintf(LOG_DEST, "\t%.4f", force->vp[i]); } fprintf(LOG_DEST, "\n"); fprintf(LOG_DEST, "\tvpd :"); for (i = 0; i <= nr; i++) { - fprintf(LOG_DEST, "\t%.4f", atmos->vpd[i]); + fprintf(LOG_DEST, "\t%.4f", force->vpd[i]); } fprintf(LOG_DEST, "\n"); fprintf(LOG_DEST, "\twind :"); for (i = 0; i <= nr; i++) { - fprintf(LOG_DEST, "\t%.4f", atmos->wind[i]); + fprintf(LOG_DEST, "\t%.4f", force->wind[i]); } fprintf(LOG_DEST, "\n"); if (options.LAKES) { fprintf(LOG_DEST, "\tchannel_in:"); for (i = 0; i <= nr; i++) { - fprintf(LOG_DEST, "\t%.4f", atmos->channel_in[i]); + fprintf(LOG_DEST, "\t%.4f", force->channel_in[i]); } fprintf(LOG_DEST, "\n"); } if (options.CARBON) { fprintf(LOG_DEST, "\tCatm :"); for (i = 0; i <= nr; i++) { - fprintf(LOG_DEST, "\t%.4f", atmos->Catm[i]); + fprintf(LOG_DEST, "\t%.4f", force->Catm[i]); } fprintf(LOG_DEST, "\n"); fprintf(LOG_DEST, "\tfdir :"); for (i = 0; i <= nr; i++) { - fprintf(LOG_DEST, "\t%.4f", atmos->fdir[i]); + fprintf(LOG_DEST, "\t%.4f", force->fdir[i]); } fprintf(LOG_DEST, "\n"); fprintf(LOG_DEST, "\tpar :"); for (i = 0; i <= nr; i++) { - fprintf(LOG_DEST, "\t%.4f", atmos->par[i]); + fprintf(LOG_DEST, "\t%.4f", force->par[i]); } fprintf(LOG_DEST, "\n"); } } + +/****************************************************************************** + * @brief Print filenames structure. + *****************************************************************************/ +void +print_filenames(filenames_struct *fnames) +{ + fprintf(LOG_DEST, "filenames:\n"); + fprintf(LOG_DEST, "\tforcing[0] : %s\n", fnames->forcing[0]); + fprintf(LOG_DEST, "\tforcing[1] : %s\n", fnames->forcing[1]); + fprintf(LOG_DEST, "\tf_path_pfx[0]: %s\n", fnames->f_path_pfx[0]); + fprintf(LOG_DEST, "\tf_path_pfx[1]: %s\n", fnames->f_path_pfx[1]); + fprintf(LOG_DEST, "\tglobal : %s\n", fnames->global); + fprintf(LOG_DEST, "\tconstants : %s\n", fnames->constants); + fprintf(LOG_DEST, "\tinit_state : %s\n", fnames->init_state); + fprintf(LOG_DEST, "\tlakeparam : %s\n", fnames->lakeparam); + fprintf(LOG_DEST, "\tresult_dir : %s\n", fnames->result_dir); + fprintf(LOG_DEST, "\tsnowband : %s\n", fnames->snowband); + fprintf(LOG_DEST, "\tsoil : %s\n", fnames->soil); + fprintf(LOG_DEST, "\tstatefile : %s\n", fnames->statefile); + fprintf(LOG_DEST, "\tveg : %s\n", fnames->veg); + fprintf(LOG_DEST, "\tveglib : %s\n", fnames->veglib); + fprintf(LOG_DEST, "\tlog_path : %s\n", fnames->log_path); +} + +/****************************************************************************** + * @brief Print file path structure. + *****************************************************************************/ +void +print_filep(filep_struct *fp) +{ + fprintf(LOG_DEST, "filep:\n"); + fprintf(LOG_DEST, "\tforcing[0] : %p\n", fp->forcing[0]); + fprintf(LOG_DEST, "\tforcing[1] : %p\n", fp->forcing[1]); + fprintf(LOG_DEST, "\tglobalparam: %p\n", fp->globalparam); + fprintf(LOG_DEST, "\tconstants : %p\n", fp->constants); + fprintf(LOG_DEST, "\tinit_state : %p\n", fp->init_state); + fprintf(LOG_DEST, "\tlakeparam : %p\n", fp->lakeparam); + fprintf(LOG_DEST, "\tsnowband : %p\n", fp->snowband); + fprintf(LOG_DEST, "\tsoilparam : %p\n", fp->soilparam); + fprintf(LOG_DEST, "\tstatefile : %p\n", fp->statefile); + fprintf(LOG_DEST, "\tveglib : %p\n", fp->veglib); + fprintf(LOG_DEST, "\tvegparam : %p\n", fp->vegparam); + fprintf(LOG_DEST, "\tlogfile : %p\n", fp->logfile); +} diff --git a/vic/drivers/classic/src/read_atmos_data.c b/vic/drivers/classic/src/read_atmos_data.c index d90acbad4..deab975e4 100644 --- a/vic/drivers/classic/src/read_atmos_data.c +++ b/vic/drivers/classic/src/read_atmos_data.c @@ -108,7 +108,7 @@ read_atmos_data(FILE *infile, // Nbytes is assumed to be the byte offset at which the data records start. fseek(infile, 0, SEEK_SET); if (feof(infile)) { - log_err("No data in the forcing file. Model stopping..."); + log_err("No data in the forcing file."); } for (i = 0; i < 4; i++) { fread(&ustmp, sizeof(unsigned short int), 1, infile); @@ -136,7 +136,7 @@ read_atmos_data(FILE *infile, fseek(infile, skip_recs * Nfields * sizeof(short int), SEEK_CUR); if (feof(infile)) { log_err("No data for the specified time period in the forcing " - "file. Model stopping..."); + "file."); } /** Read BINARY forcing data **/ @@ -216,7 +216,7 @@ read_atmos_data(FILE *infile, for (i = 0; i < skip_recs; i++) { if (fgets(str, MAXSTRING, infile) == NULL) { log_err("No data for the specified time period in the forcing " - "file. Model stopping..."); + "file."); } } diff --git a/vic/drivers/classic/src/read_forcing_data.c b/vic/drivers/classic/src/read_forcing_data.c index 4b289db8f..cfaec814c 100644 --- a/vic/drivers/classic/src/read_forcing_data.c +++ b/vic/drivers/classic/src/read_forcing_data.c @@ -53,17 +53,19 @@ read_forcing_data(FILE **infile, if (i != ALBEDO && i != LAI_IN && i != FCANOPY) { forcing_data[i] = calloc(global_param.nrecs * NF, sizeof(*(forcing_data[i]))); - check_alloc_status(forcing_data[i], "Memory allocation error."); + check_alloc_status(forcing_data[i], "Memory allocation error."); } else { (*veg_hist_data)[i] = calloc(param_set.TYPE[i].N_ELEM, sizeof(*((*veg_hist_data)[i]))); - check_alloc_status((*veg_hist_data)[i], "Memory allocation error."); + check_alloc_status((*veg_hist_data)[i], + "Memory allocation error."); for (j = 0; j < param_set.TYPE[i].N_ELEM; j++) { (*veg_hist_data)[i][j] = calloc(global_param.nrecs * NF, sizeof(*((*veg_hist_data)[i] [j]))); - check_alloc_status((*veg_hist_data)[i][j], "Memory allocation error."); + check_alloc_status((*veg_hist_data)[i][j], + "Memory allocation error."); } } } diff --git a/vic/drivers/classic/src/read_soilparam.c b/vic/drivers/classic/src/read_soilparam.c index dd9a54d2c..a71abd14c 100644 --- a/vic/drivers/classic/src/read_soilparam.c +++ b/vic/drivers/classic/src/read_soilparam.c @@ -29,10 +29,11 @@ /****************************************************************************** * @brief This routine reads soil parameters for each grid cell. *****************************************************************************/ -soil_con_struct -read_soilparam(FILE *soilparam, - char *RUN_MODEL, - char *MODEL_DONE) +void +read_soilparam(FILE *soilparam, + soil_con_struct *temp, + bool *RUN_MODEL, + bool *MODEL_DONE) { void ttrim(char *string); extern option_struct options; @@ -61,7 +62,6 @@ read_soilparam(FILE *soilparam, double zwt_prime, zwt_prime_eff; double tmp_moist; double w_avg; - soil_con_struct temp; double Zsum, dp; double tmpdp, tmpadj, Bexp; size_t k; @@ -91,7 +91,7 @@ read_soilparam(FILE *soilparam, if ((token = strtok(tmpline, delimiters)) == NULL) { log_err("Can't find values for CELL NUMBER in soil file"); } - sscanf(token, "%d", &temp.gridcel); + sscanf(token, "%d", &(temp->gridcel)); token = strtok(NULL, delimiters); while (token != NULL && (length = strlen(token)) == 0) { token = strtok(NULL, delimiters); @@ -99,7 +99,7 @@ read_soilparam(FILE *soilparam, if (token == NULL) { log_err("Can't find values for CELL LATITUDE in soil file"); } - sscanf(token, "%lf", &temp.lat); + sscanf(token, "%lf", &(temp->lat)); token = strtok(NULL, delimiters); while (token != NULL && (length = strlen(token)) == 0) { token = strtok(NULL, delimiters); @@ -107,7 +107,7 @@ read_soilparam(FILE *soilparam, if (token == NULL) { log_err("Can't find values for CELL LONGITUDE in soil file"); } - sscanf(token, "%lf", &temp.lng); + sscanf(token, "%lf", &(temp->lng)); /* read infiltration parameter */ token = strtok(NULL, delimiters); @@ -117,10 +117,10 @@ read_soilparam(FILE *soilparam, if (token == NULL) { log_err("Can't find values for INFILTRATION in soil file"); } - sscanf(token, "%lf", &temp.b_infilt); - if (temp.b_infilt <= 0) { + sscanf(token, "%lf", &(temp->b_infilt)); + if (temp->b_infilt <= 0) { log_err("b_infilt (%f) in soil file is <= 0; b_infilt must " - "be positive", temp.b_infilt); + "be positive", temp->b_infilt); } /* read fraction of baseflow rate */ @@ -132,7 +132,7 @@ read_soilparam(FILE *soilparam, log_err("Can't find values for FRACTION OF BASEFLOW RATE " "in soil file"); } - sscanf(token, "%lf", &temp.Ds); + sscanf(token, "%lf", &(temp->Ds)); /* read maximum baseflow rate */ token = strtok(NULL, delimiters); @@ -143,7 +143,7 @@ read_soilparam(FILE *soilparam, log_err("Can't find values for MAXIMUM BASEFLOW RATE in " "soil file"); } - sscanf(token, "%lf", &temp.Dsmax); + sscanf(token, "%lf", &(temp->Dsmax)); /* read fraction of bottom soil layer moisture */ token = strtok(NULL, delimiters); @@ -154,7 +154,7 @@ read_soilparam(FILE *soilparam, log_err("Can't find values for FRACTION OF BOTTOM SOIL LAYER " "MOISTURE in soil file"); } - sscanf(token, "%lf", &temp.Ws); + sscanf(token, "%lf", &(temp->Ws)); /* read exponential */ token = strtok(NULL, delimiters); @@ -164,7 +164,7 @@ read_soilparam(FILE *soilparam, if (token == NULL) { log_err("Can't find values for EXPONENTIAL in soil file"); } - sscanf(token, "%lf", &temp.c); + sscanf(token, "%lf", &(temp->c)); /* read expt for each layer */ for (layer = 0; layer < options.Nlayer; layer++) { @@ -176,10 +176,10 @@ read_soilparam(FILE *soilparam, log_err("Can't find values for EXPT for layer %zu in " "soil file", layer); } - sscanf(token, "%lf", &temp.expt[layer]); - if (temp.expt[layer] < 3.0) { + sscanf(token, "%lf", &(temp->expt)[layer]); + if (temp->expt[layer] < 3.0) { log_err("Exponent in layer %zu is %f < 3.0; This must be " - "> 3.0", layer, temp.expt[layer]); + "> 3.0", layer, temp->expt[layer]); } } @@ -193,7 +193,7 @@ read_soilparam(FILE *soilparam, log_err("Can't find values for SATURATED HYDRAULIC " "CONDUCTIVITY for layer %zu in soil file", layer); } - sscanf(token, "%lf", &temp.Ksat[layer]); + sscanf(token, "%lf", &(temp->Ksat)[layer]); } /* read layer phi_s */ @@ -206,7 +206,7 @@ read_soilparam(FILE *soilparam, log_err("Can't find values for PHI_S for layer %zu in " "soil file", layer); } - sscanf(token, "%lf", &temp.phi_s[layer]); + sscanf(token, "%lf", &(temp->phi_s)[layer]); } /* read layer initial moisture */ @@ -219,10 +219,10 @@ read_soilparam(FILE *soilparam, log_err("Can't find values for INITIAL MOISTURE for " "layer %zu in soil file", layer); } - sscanf(token, "%lf", &temp.init_moist[layer]); - if (temp.init_moist[layer] < 0.) { + sscanf(token, "%lf", &(temp->init_moist)[layer]); + if (temp->init_moist[layer] < 0.) { log_err("Initial moisture for layer %zu cannot be " - "negative (%f)", layer, temp.init_moist[layer]); + "negative (%f)", layer, temp->init_moist[layer]); } } @@ -235,7 +235,7 @@ read_soilparam(FILE *soilparam, log_err("Can't find values for CELL MEAN ELEVATION in soil " "file"); } - sscanf(token, "%lf", &temp.elevation); + sscanf(token, "%lf", &(temp->elevation)); /* soil layer thicknesses */ for (layer = 0; layer < options.Nlayer; layer++) { @@ -247,12 +247,12 @@ read_soilparam(FILE *soilparam, log_err("Can't find values for LAYER THICKNESS for " "layer %zu in soil file", layer); } - sscanf(token, "%lf", &temp.depth[layer]); + sscanf(token, "%lf", &(temp->depth)[layer]); } /* round soil layer thicknesses to nearest mm */ for (layer = 0; layer < options.Nlayer; layer++) { - temp.depth[layer] = - round(temp.depth[layer] * MM_PER_M) / MM_PER_M; + temp->depth[layer] = + round(temp->depth[layer] * MM_PER_M) / MM_PER_M; } /* read average soil temperature */ @@ -264,12 +264,12 @@ read_soilparam(FILE *soilparam, log_err("Can't find values for AVERAGE SOIL TEMPERATURE in " "soil file"); } - sscanf(token, "%lf", &temp.avg_temp); + sscanf(token, "%lf", &(temp->avg_temp)); if ((options.FULL_ENERGY || options.LAKES) && - (temp.avg_temp > 100. || temp.avg_temp < -50)) { + (temp->avg_temp > 100. || temp->avg_temp < -50)) { log_err("Need valid average soil temperature in degrees C to " "run. Full Energy model, %f is not acceptable.", - temp.avg_temp); + temp->avg_temp); } /* read soil damping depth */ @@ -281,7 +281,7 @@ read_soilparam(FILE *soilparam, log_err("Can't find values for SOIL DAMPING DEPTH in soil " "file"); } - sscanf(token, "%lf", &temp.dp); + sscanf(token, "%lf", &(temp->dp)); /* read layer bubbling pressure */ for (layer = 0; layer < options.Nlayer; layer++) { @@ -293,12 +293,12 @@ read_soilparam(FILE *soilparam, log_err("Can't find values for BUBBLING PRESSURE for " "layer %zu in soil file", layer); } - sscanf(token, "%lf", &temp.bubble[layer]); + sscanf(token, "%lf", &(temp->bubble)[layer]); if ((options.FULL_ENERGY || - options.FROZEN_SOIL) && temp.bubble[layer] < 0) { + options.FROZEN_SOIL) && temp->bubble[layer] < 0) { log_err("Bubbling pressure in layer %zu is %f < 0; " "This must be positive for FULL_ENERGY = true or " - "FROZEN_SOIL = true", layer, temp.bubble[layer]); + "FROZEN_SOIL = true", layer, temp->bubble[layer]); } } @@ -312,12 +312,12 @@ read_soilparam(FILE *soilparam, log_err("Can't find values for QUARTZ CONTENT for " "layer %zu in soil file", layer); } - sscanf(token, "%lf", &temp.quartz[layer]); + sscanf(token, "%lf", &(temp->quartz)[layer]); if (options.FULL_ENERGY && - (temp.quartz[layer] > 1. || temp.quartz[layer] < 0)) { + (temp->quartz[layer] > 1. || temp->quartz[layer] < 0)) { log_err("Need valid quartz content as a fraction to run " "Full Energy model, %f is not acceptable.", - temp.quartz[layer]); + temp->quartz[layer]); } } @@ -331,10 +331,10 @@ read_soilparam(FILE *soilparam, log_err("Can't find values for mineral BULK DENSITY " "for layer %zu in soil file", layer); } - sscanf(token, "%lf", &temp.bulk_dens_min[layer]); - if (temp.bulk_dens_min[layer] <= 0) { + sscanf(token, "%lf", &(temp->bulk_dens_min)[layer]); + if (temp->bulk_dens_min[layer] <= 0) { log_err("layer %zu mineral bulk density (%f) must " - "be > 0", layer, temp.bulk_dens_min[layer]); + "be > 0", layer, temp->bulk_dens_min[layer]); } } @@ -348,16 +348,16 @@ read_soilparam(FILE *soilparam, log_err("Can't find values for mineral SOIL DENSITY " "for layer %zu in soil file", layer); } - sscanf(token, "%lf", &temp.soil_dens_min[layer]); - if (temp.soil_dens_min[layer] <= 0) { + sscanf(token, "%lf", &(temp->soil_dens_min)[layer]); + if (temp->soil_dens_min[layer] <= 0) { log_err("layer %zu mineral soil density (%f) must " - "be > 0", layer, temp.soil_dens_min[layer]); + "be > 0", layer, temp->soil_dens_min[layer]); } - if (temp.bulk_dens_min[layer] >= temp.soil_dens_min[layer]) { + if (temp->bulk_dens_min[layer] >= temp->soil_dens_min[layer]) { log_err("layer %zu mineral bulk density (%f) must " "be less than mineral soil density (%f)", layer, - temp.bulk_dens_min[layer], - temp.soil_dens_min[layer]); + temp->bulk_dens_min[layer], + temp->soil_dens_min[layer]); } } @@ -372,12 +372,12 @@ read_soilparam(FILE *soilparam, log_err("Can't find values for ORGANIC CONTENT for " "layer %zu in soil file", layer); } - sscanf(token, "%lf", &temp.organic[layer]); - if (temp.organic[layer] > 1. || temp.organic[layer] < 0) { + sscanf(token, "%lf", &(temp->organic)[layer]); + if (temp->organic[layer] > 1. || temp->organic[layer] < 0) { log_err("Need valid volumetric organic soil " "fraction when options.ORGANIC_FRACT is set " "to true. %f is not acceptable.", - temp.organic[layer]); + temp->organic[layer]); } } @@ -391,14 +391,14 @@ read_soilparam(FILE *soilparam, log_err("Can't find values for organic BULK " "DENSITY for layer %zu in soil file", layer); } - sscanf(token, "%lf", &temp.bulk_dens_org[layer]); - if (temp.bulk_dens_org[layer] <= 0 && temp.organic[layer] > + sscanf(token, "%lf", &(temp->bulk_dens_org)[layer]); + if (temp->bulk_dens_org[layer] <= 0 && temp->organic[layer] > 0) { log_warn("layer %zu organic bulk density (%f) must " "be > 0; setting to mineral bulk density " - "(%f)", layer, temp.bulk_dens_org[layer], - temp.bulk_dens_min[layer]); - temp.bulk_dens_org[layer] = temp.bulk_dens_min[layer]; + "(%f)", layer, temp->bulk_dens_org[layer], + temp->bulk_dens_min[layer]); + temp->bulk_dens_org[layer] = temp->bulk_dens_min[layer]; } } @@ -412,29 +412,29 @@ read_soilparam(FILE *soilparam, log_err("Can't find values for organic SOIL DENSITY for " "layer %zu in soil file", layer); } - sscanf(token, "%lf", &temp.soil_dens_org[layer]); - if (temp.soil_dens_org[layer] <= 0 && temp.organic[layer] > + sscanf(token, "%lf", &(temp->soil_dens_org)[layer]); + if (temp->soil_dens_org[layer] <= 0 && temp->organic[layer] > 0) { log_warn("layer %zu organic soil density (%f) must be " "> 0; setting to mineral soil density (%f)", - layer, temp.soil_dens_org[layer], - temp.soil_dens_min[layer]); - temp.soil_dens_org[layer] = temp.soil_dens_min[layer]; + layer, temp->soil_dens_org[layer], + temp->soil_dens_min[layer]); + temp->soil_dens_org[layer] = temp->soil_dens_min[layer]; } - if (temp.organic[layer] > 0 && temp.bulk_dens_org[layer] >= - temp.soil_dens_org[layer]) { + if (temp->organic[layer] > 0 && temp->bulk_dens_org[layer] >= + temp->soil_dens_org[layer]) { log_err("layer %zu organic bulk density (%f) " "must be less than organic soil density (%f)", - layer, temp.bulk_dens_org[layer], - temp.soil_dens_org[layer]); + layer, temp->bulk_dens_org[layer], + temp->soil_dens_org[layer]); } } } else { for (layer = 0; layer < options.Nlayer; layer++) { - temp.organic[layer] = 0.0; - temp.bulk_dens_org[layer] = MISSING; - temp.soil_dens_org[layer] = MISSING; + temp->organic[layer] = 0.0; + temp->bulk_dens_org[layer] = MISSING; + temp->soil_dens_org[layer] = MISSING; } } @@ -482,13 +482,13 @@ read_soilparam(FILE *soilparam, if (token == NULL) { log_err("Can't find values for SOIL ROUGHNESS in soil file"); } - sscanf(token, "%lf", &temp.rough); + sscanf(token, "%lf", &(temp->rough)); /* Overwrite default bare soil aerodynamic resistance parameters with the values taken from the soil parameter file */ for (j = 0; j < MONTHS_PER_YEAR; j++) { - veg_lib[veg_lib[0].NVegLibTypes].roughness[j] = temp.rough; - veg_lib[veg_lib[0].NVegLibTypes].displacement[j] = temp.rough * + veg_lib[veg_lib[0].NVegLibTypes].roughness[j] = temp->rough; + veg_lib[veg_lib[0].NVegLibTypes].displacement[j] = temp->rough * 0.667 / 0.123; } @@ -501,7 +501,7 @@ read_soilparam(FILE *soilparam, if (token == NULL) { log_err("Can't find values for SNOW ROUGHNESS in soil file"); } - sscanf(token, "%lf", &temp.snow_rough); + sscanf(token, "%lf", &(temp->snow_rough)); /* read cell annual precipitation */ token = strtok(NULL, delimiters); @@ -511,7 +511,7 @@ read_soilparam(FILE *soilparam, if (token == NULL) { log_err("Can't find values for ANNUAL PRECIPITATION in soil file"); } - sscanf(token, "%lf", &temp.annual_prec); + sscanf(token, "%lf", &(temp->annual_prec)); /* read layer residual moisture content */ for (layer = 0; layer < options.Nlayer; layer++) { @@ -523,7 +523,7 @@ read_soilparam(FILE *soilparam, log_err("Can't find values for RESIDUAL MOISTURE CONTENT for " "layer %zu in soil file", layer); } - sscanf(token, "%lf", &temp.resid_moist[layer]); + sscanf(token, "%lf", &(temp->resid_moist)[layer]); } /* read frozen soil active flag */ @@ -536,7 +536,7 @@ read_soilparam(FILE *soilparam, "soil file"); } sscanf(token, "%d", &tempint); - temp.FS_ACTIVE = (char)tempint; + temp->FS_ACTIVE = (char)tempint; /* read minimum snow depth for full coverage */ if (options.SPATIAL_SNOW) { @@ -548,10 +548,10 @@ read_soilparam(FILE *soilparam, log_err("Can't find values for SPATIAL SNOW in soil file"); } sscanf(token, "%lf", &tempdbl); - temp.max_snow_distrib_slope = tempdbl; + temp->max_snow_distrib_slope = tempdbl; } else { - temp.max_snow_distrib_slope = 0; + temp->max_snow_distrib_slope = 0; } /* read slope of frozen soil distribution */ @@ -564,10 +564,10 @@ read_soilparam(FILE *soilparam, log_err("Can't find values for SPATIAL FROST in soil file"); } sscanf(token, "%lf", &tempdbl); - temp.frost_slope = tempdbl; + temp->frost_slope = tempdbl; } else { - temp.frost_slope = 0; + temp->frost_slope = 0; } /* If specified, read cell average July air temperature in the final @@ -582,7 +582,7 @@ read_soilparam(FILE *soilparam, "soil file"); } sscanf(token, "%lf", &tempdbl); - temp.avgJulyAirTemp = tempdbl; + temp->avgJulyAirTemp = tempdbl; } /******************************************* @@ -593,73 +593,73 @@ read_soilparam(FILE *soilparam, Compute Soil Layer Properties *******************************************/ for (layer = 0; layer < options.Nlayer; layer++) { - temp.bulk_density[layer] = + temp->bulk_density[layer] = (1 - - temp.organic[layer]) * temp.bulk_dens_min[layer] + - temp.organic[layer] * temp.bulk_dens_org[layer]; - temp.soil_density[layer] = + temp->organic[layer]) * temp->bulk_dens_min[layer] + + temp->organic[layer] * temp->bulk_dens_org[layer]; + temp->soil_density[layer] = (1 - - temp.organic[layer]) * temp.soil_dens_min[layer] + - temp.organic[layer] * temp.soil_dens_org[layer]; - if (temp.resid_moist[layer] == MISSING) { - temp.resid_moist[layer] = param.SOIL_RESID_MOIST; + temp->organic[layer]) * temp->soil_dens_min[layer] + + temp->organic[layer] * temp->soil_dens_org[layer]; + if (temp->resid_moist[layer] == MISSING) { + temp->resid_moist[layer] = param.SOIL_RESID_MOIST; } - temp.porosity[layer] = 1.0 - temp.bulk_density[layer] / - temp.soil_density[layer]; - temp.max_moist[layer] = temp.depth[layer] * - temp.porosity[layer] * MM_PER_M; + temp->porosity[layer] = 1.0 - temp->bulk_density[layer] / + temp->soil_density[layer]; + temp->max_moist[layer] = temp->depth[layer] * + temp->porosity[layer] * MM_PER_M; } /********************************************** Validate Soil Layer Thicknesses **********************************************/ for (layer = 0; layer < options.Nlayer; layer++) { - if (temp.depth[layer] < MINSOILDEPTH) { + if (temp->depth[layer] < MINSOILDEPTH) { log_err("Model will not function with layer %zu " - "depth %f < %f m.", layer, temp.depth[layer], + "depth %f < %f m.", layer, temp->depth[layer], MINSOILDEPTH); } } - if (temp.depth[0] > temp.depth[1]) { + if (temp->depth[0] > temp->depth[1]) { log_err("Model will not function with layer %d depth" - "(%f m) > layer %d depth (%f m).", 0, temp.depth[0], - 1, temp.depth[1]); + "(%f m) > layer %d depth (%f m).", 0, temp->depth[0], + 1, temp->depth[1]); } /********************************************** Compute Maximum Infiltration for Upper Layers **********************************************/ if (options.Nlayer == 2) { - temp.max_infil = (1.0 + temp.b_infilt) * temp.max_moist[0]; + temp->max_infil = (1.0 + temp->b_infilt) * temp->max_moist[0]; } else { - temp.max_infil = + temp->max_infil = (1.0 + - temp.b_infilt) * (temp.max_moist[0] + temp.max_moist[1]); + temp->b_infilt) * (temp->max_moist[0] + temp->max_moist[1]); } /**************************************************************** Compute Soil Layer Critical and Wilting Point Moisture Contents ****************************************************************/ for (layer = 0; layer < options.Nlayer; layer++) { - temp.Wcr[layer] = Wcr_FRACT[layer] * temp.max_moist[layer]; - temp.Wpwp[layer] = Wpwp_FRACT[layer] * temp.max_moist[layer]; - if (temp.Wpwp[layer] > temp.Wcr[layer]) { + temp->Wcr[layer] = Wcr_FRACT[layer] * temp->max_moist[layer]; + temp->Wpwp[layer] = Wpwp_FRACT[layer] * temp->max_moist[layer]; + if (temp->Wpwp[layer] > temp->Wcr[layer]) { log_err("Calculated wilting point moisture (%f mm) is " "greater than calculated critical point moisture " "(%f mm) for layer %zu.\n\tIn the soil parameter " "file, Wpwp_FRACT MUST be <= Wcr_FRACT.", - temp.Wpwp[layer], temp.Wcr[layer], layer); + temp->Wpwp[layer], temp->Wcr[layer], layer); } - if (temp.Wpwp[layer] < temp.resid_moist[layer] * - temp.depth[layer] * MM_PER_M) { + if (temp->Wpwp[layer] < temp->resid_moist[layer] * + temp->depth[layer] * MM_PER_M) { log_err("Calculated wilting point moisture (%f mm) is " "less than calculated residual moisture (%f mm) " "for layer %zu.\n\tIn the soil parameter file, " "Wpwp_FRACT MUST be >= resid_moist / (1.0 - " "bulk_density/soil_density).", - temp.Wpwp[layer], temp.resid_moist[layer] * - temp.depth[layer] * MM_PER_M, layer); + temp->Wpwp[layer], temp->resid_moist[layer] * + temp->depth[layer] * MM_PER_M, layer); } } @@ -667,29 +667,29 @@ read_soilparam(FILE *soilparam, Validate Spatial Snow/Frost Params **********************************************/ if (options.SPATIAL_SNOW) { - if (temp.max_snow_distrib_slope < 0.0) { + if (temp->max_snow_distrib_slope < 0.0) { log_err("max_snow_distrib_slope (%f) must be positive.", - temp.max_snow_distrib_slope); + temp->max_snow_distrib_slope); } } if (options.SPATIAL_FROST) { - if (temp.frost_slope < 0.0) { + if (temp->frost_slope < 0.0) { log_err("frost_slope (%f) must be positive.", - temp.frost_slope); + temp->frost_slope); } } for (k = 0; k < options.Nfrost; k++) { if (options.Nfrost == 1) { - temp.frost_fract[k] = 1.; + temp->frost_fract[k] = 1.; } else if (options.Nfrost == 2) { - temp.frost_fract[k] = 0.5; + temp->frost_fract[k] = 0.5; } else { - temp.frost_fract[k] = 1. / (options.Nfrost - 1); + temp->frost_fract[k] = 1. / (options.Nfrost - 1); if (k == 0 || k == options.Nfrost - 1) { - temp.frost_fract[k] /= 2.; + temp->frost_fract[k] /= 2.; } } } @@ -701,28 +701,28 @@ read_soilparam(FILE *soilparam, *************************************************/ if (options.BASEFLOW == NIJSSEN2001) { layer = options.Nlayer - 1; - temp.Dsmax = temp.Dsmax * - pow( - (double) (1. / (temp.max_moist[layer] - temp.Ws)), - -temp.c) + - temp.Ds * temp.max_moist[layer]; - temp.Ds = temp.Ds * temp.Ws / temp.Dsmax; - temp.Ws = temp.Ws / temp.max_moist[layer]; + temp->Dsmax = temp->Dsmax * + pow( + (double) (1. / (temp->max_moist[layer] - temp->Ws)), + -temp->c) + + temp->Ds * temp->max_moist[layer]; + temp->Ds = temp->Ds * temp->Ws / temp->Dsmax; + temp->Ws = temp->Ws / temp->max_moist[layer]; } // Soil thermal node thicknesses and positions Nnodes = options.Nnode; - dp = temp.dp; + dp = temp->dp; if (options.QUICK_FLUX) { /* node thicknesses */ - temp.dz_node[0] = temp.depth[0]; - temp.dz_node[1] = temp.depth[0]; - temp.dz_node[2] = 2. * (dp - 1.5 * temp.depth[0]); + temp->dz_node[0] = temp->depth[0]; + temp->dz_node[1] = temp->depth[0]; + temp->dz_node[2] = 2. * (dp - 1.5 * temp->depth[0]); /* node depths (positions) */ - temp.Zsum_node[0] = 0; - temp.Zsum_node[1] = temp.depth[0]; - temp.Zsum_node[2] = dp; + temp->Zsum_node[0] = 0; + temp->Zsum_node[1] = temp->depth[0]; + temp->Zsum_node[2] = dp; } else { if (!options.EXP_TRANS) { @@ -733,26 +733,28 @@ read_soilparam(FILE *soilparam, between the damping depth and twice the depth of the first layer. */ - temp.dz_node[0] = temp.depth[0]; - temp.dz_node[1] = temp.depth[0]; - temp.dz_node[2] = temp.depth[0]; - temp.Zsum_node[0] = 0; - temp.Zsum_node[1] = temp.depth[0]; - Zsum = 2. * temp.depth[0]; - temp.Zsum_node[2] = Zsum; - tmpdp = dp - temp.depth[0] * 2.5; + temp->dz_node[0] = temp->depth[0]; + temp->dz_node[1] = temp->depth[0]; + temp->dz_node[2] = temp->depth[0]; + temp->Zsum_node[0] = 0; + temp->Zsum_node[1] = temp->depth[0]; + Zsum = 2. * temp->depth[0]; + temp->Zsum_node[2] = Zsum; + tmpdp = dp - temp->depth[0] * 2.5; tmpadj = 3.5; for (k = 3; k < Nnodes - 1; k++) { - temp.dz_node[k] = tmpdp / (((double) Nnodes - tmpadj)); - Zsum += (temp.dz_node[k] + temp.dz_node[k - 1]) / 2.; - temp.Zsum_node[k] = Zsum; + temp->dz_node[k] = tmpdp / (((double) Nnodes - tmpadj)); + Zsum += (temp->dz_node[k] + temp->dz_node[k - 1]) / 2.; + temp->Zsum_node[k] = Zsum; } - temp.dz_node[Nnodes - - 1] = (dp - Zsum - temp.dz_node[Nnodes - 2] / 2.) * - 2.; - Zsum += (temp.dz_node[Nnodes - 2] + temp.dz_node[Nnodes - 1]) / - 2.; - temp.Zsum_node[Nnodes - 1] = Zsum; + temp->dz_node[Nnodes - + 1] = + (dp - Zsum - temp->dz_node[Nnodes - 2] / 2.) * + 2.; + Zsum += + (temp->dz_node[Nnodes - 2] + temp->dz_node[Nnodes - 1]) / + 2.; + temp->Zsum_node[Nnodes - 1] = Zsum; if ((int) (Zsum * MM_PER_M + 0.5) != (int) (dp * MM_PER_M + 0.5)) { log_err("Sum of thermal node thicknesses (%f) " @@ -786,60 +788,60 @@ read_soilparam(FILE *soilparam, exp(0.2 * (Nnodes - 1)) + 1); } for (k = 0; k <= Nnodes - 1; k++) { - temp.Zsum_node[k] = expf(Bexp * k) - 1.; + temp->Zsum_node[k] = expf(Bexp * k) - 1.; } - if (temp.Zsum_node[0] > temp.depth[0]) { + if (temp->Zsum_node[0] > temp->depth[0]) { log_err("Depth of first thermal node (%f) in " "initialize_model_state is greater " "than depth of first soil layer (%f); " "increase the number of nodes or " "decrease the thermal damping depth " - "dp (%f)", temp.Zsum_node[0], temp.depth[0], dp); + "dp (%f)", temp->Zsum_node[0], temp->depth[0], dp); } // top node k = 0; - temp.dz_node[k] = temp.Zsum_node[k + 1] - temp.Zsum_node[k]; + temp->dz_node[k] = temp->Zsum_node[k + 1] - temp->Zsum_node[k]; // middle nodes for (k = 1; k < Nnodes - 1; k++) { - temp.dz_node[k] = - (temp.Zsum_node[k + 1] - temp.Zsum_node[k]) / 2. + - (temp.Zsum_node[k] - temp.Zsum_node[k - 1]) / 2.; + temp->dz_node[k] = + (temp->Zsum_node[k + 1] - temp->Zsum_node[k]) / 2. + + (temp->Zsum_node[k] - temp->Zsum_node[k - 1]) / 2.; } // bottom node k = Nnodes - 1; - temp.dz_node[k] = temp.Zsum_node[k] - temp.Zsum_node[k - 1]; + temp->dz_node[k] = temp->Zsum_node[k] - temp->Zsum_node[k - 1]; } // end if !EXP_TRANS } /******************************************************************* Calculate grid cell area. ******************************************************************/ - compute_cell_area(&temp); + compute_cell_area(temp); /************************************************* Allocate and Initialize Snow Band Parameters *************************************************/ Nbands = options.SNOW_BAND; - temp.AreaFract = calloc(Nbands, sizeof(*(temp.AreaFract))); - check_alloc_status(temp.AreaFract, "Memory allocation error."); - temp.BandElev = calloc(Nbands, sizeof(*(temp.BandElev))); - check_alloc_status(temp.BandElev, "Memory allocation error."); - temp.Tfactor = calloc(Nbands, sizeof(*(temp.Tfactor))); - check_alloc_status(temp.Tfactor, "Memory allocation error."); - temp.Pfactor = calloc(Nbands, sizeof(*(temp.Pfactor))); - check_alloc_status(temp.Pfactor, "Memory allocation error."); - temp.AboveTreeLine = calloc(Nbands, sizeof(*(temp.AboveTreeLine))); - check_alloc_status(temp.AboveTreeLine, "Memory allocation error."); + temp->AreaFract = calloc(Nbands, sizeof(*(temp->AreaFract))); + check_alloc_status(temp->AreaFract, "Memory allocation error."); + temp->BandElev = calloc(Nbands, sizeof(*(temp->BandElev))); + check_alloc_status(temp->BandElev, "Memory allocation error."); + temp->Tfactor = calloc(Nbands, sizeof(*(temp->Tfactor))); + check_alloc_status(temp->Tfactor, "Memory allocation error."); + temp->Pfactor = calloc(Nbands, sizeof(*(temp->Pfactor))); + check_alloc_status(temp->Pfactor, "Memory allocation error."); + temp->AboveTreeLine = calloc(Nbands, sizeof(*(temp->AboveTreeLine))); + check_alloc_status(temp->AboveTreeLine, "Memory allocation error."); /** Set default values for factors to use unmodified forcing data **/ for (band = 0; band < Nbands; band++) { - temp.AreaFract[band] = 0.; - temp.BandElev[band] = temp.elevation; - temp.Tfactor[band] = 0.; - temp.Pfactor[band] = 1.; + temp->AreaFract[band] = 0.; + temp->BandElev[band] = temp->elevation; + temp->Tfactor[band] = 0.; + temp->Pfactor[band] = 1.; } - temp.AreaFract[0] = 1.; + temp->AreaFract[0] = 1.; /************************************************* Compute soil moistures for various values of water table depth @@ -882,36 +884,36 @@ read_soilparam(FILE *soilparam, /* Individual layers */ tmp_depth = 0; for (layer = 0; layer < options.Nlayer; layer++) { - b = 0.5 * (temp.expt[layer] - 3); - bubble = temp.bubble[layer]; - tmp_resid_moist = temp.resid_moist[layer] * temp.depth[layer] * + b = 0.5 * (temp->expt[layer] - 3); + bubble = temp->bubble[layer]; + tmp_resid_moist = temp->resid_moist[layer] * temp->depth[layer] * MM_PER_M; // in mm zwt_prime = 0; // depth of free water surface below top of layer (not yet elevation) for (i = 0; i < MAX_ZWTVMOIST; i++) { - temp.zwtvmoist_zwt[layer][i] = -tmp_depth * CM_PER_M - - zwt_prime; // elevation (cm) relative to soil surface - w_avg = (temp.depth[layer] * CM_PER_M - zwt_prime - + temp->zwtvmoist_zwt[layer][i] = -tmp_depth * CM_PER_M - + zwt_prime; // elevation (cm) relative to soil surface + w_avg = (temp->depth[layer] * CM_PER_M - zwt_prime - (b / (b - 1)) * bubble * (1 - pow((zwt_prime + bubble) / bubble, (b - 1) / b))) / - (temp.depth[layer] * CM_PER_M); // in cm + (temp->depth[layer] * CM_PER_M); // in cm if (w_avg < 0) { w_avg = 0; } if (w_avg > 1) { w_avg = 1; } - temp.zwtvmoist_moist[layer][i] = w_avg * - (temp.max_moist[layer] - - tmp_resid_moist) + - tmp_resid_moist; - zwt_prime += temp.depth[layer] * CM_PER_M / + temp->zwtvmoist_moist[layer][i] = w_avg * + (temp->max_moist[layer] - + tmp_resid_moist) + + tmp_resid_moist; + zwt_prime += temp->depth[layer] * CM_PER_M / (MAX_ZWTVMOIST - 1); // in cm } - tmp_depth += temp.depth[layer]; + tmp_depth += temp->depth[layer]; } /* Top N-1 layers lumped together (with average soil properties) */ @@ -921,18 +923,18 @@ read_soilparam(FILE *soilparam, tmp_max_moist = 0; tmp_resid_moist = 0; for (layer = 0; layer < options.Nlayer - 1; layer++) { - b += 0.5 * (temp.expt[layer] - 3) * temp.depth[layer]; - bubble += temp.bubble[layer] * temp.depth[layer]; - tmp_max_moist += temp.max_moist[layer]; // total max_moist - tmp_resid_moist += temp.resid_moist[layer] * temp.depth[layer] * + b += 0.5 * (temp->expt[layer] - 3) * temp->depth[layer]; + bubble += temp->bubble[layer] * temp->depth[layer]; + tmp_max_moist += temp->max_moist[layer]; // total max_moist + tmp_resid_moist += temp->resid_moist[layer] * temp->depth[layer] * MM_PER_M; // total resid_moist in mm - tmp_depth += temp.depth[layer]; + tmp_depth += temp->depth[layer]; } b /= tmp_depth; // average b bubble /= tmp_depth; // average bubble zwt_prime = 0; // depth of free water surface below top of layer (not yet elevation) for (i = 0; i < MAX_ZWTVMOIST; i++) { - temp.zwtvmoist_zwt[options.Nlayer][i] = -zwt_prime; // elevation (cm) relative to soil surface + temp->zwtvmoist_zwt[options.Nlayer][i] = -zwt_prime; // elevation (cm) relative to soil surface w_avg = (tmp_depth * CM_PER_M - zwt_prime - (b / (b - @@ -946,45 +948,45 @@ read_soilparam(FILE *soilparam, if (w_avg > 1) { w_avg = 1; } - temp.zwtvmoist_moist[options.Nlayer][i] = w_avg * - (tmp_max_moist - - tmp_resid_moist) + - tmp_resid_moist; + temp->zwtvmoist_moist[options.Nlayer][i] = w_avg * + (tmp_max_moist - + tmp_resid_moist) + + tmp_resid_moist; zwt_prime += tmp_depth * CM_PER_M / (MAX_ZWTVMOIST - 1); // in cm } /* Compute zwt by taking total column soil moisture and filling column from bottom up */ tmp_depth = 0; for (layer = 0; layer < options.Nlayer; layer++) { - tmp_depth += temp.depth[layer]; + tmp_depth += temp->depth[layer]; } zwt_prime = 0; // depth of free water surface below soil surface (not yet elevation) for (i = 0; i < MAX_ZWTVMOIST; i++) { - temp.zwtvmoist_zwt[options.Nlayer + 1][i] = -zwt_prime; // elevation (cm) relative to soil surface + temp->zwtvmoist_zwt[options.Nlayer + 1][i] = -zwt_prime; // elevation (cm) relative to soil surface // Integrate w_avg in pieces if (zwt_prime == 0) { tmp_moist = 0; for (layer = 0; layer < options.Nlayer; layer++) { - tmp_moist += temp.max_moist[layer]; + tmp_moist += temp->max_moist[layer]; } - temp.zwtvmoist_moist[options.Nlayer + 1][i] = tmp_moist; + temp->zwtvmoist_moist[options.Nlayer + 1][i] = tmp_moist; } else { tmp_moist = 0; layer = options.Nlayer - 1; - tmp_depth2 = tmp_depth - temp.depth[layer]; + tmp_depth2 = tmp_depth - temp->depth[layer]; while (layer > 0 && zwt_prime <= tmp_depth2 * CM_PER_M) { - tmp_moist += temp.max_moist[layer]; + tmp_moist += temp->max_moist[layer]; layer--; - tmp_depth2 -= temp.depth[layer]; + tmp_depth2 -= temp->depth[layer]; } w_avg = - (tmp_depth2 * CM_PER_M + temp.depth[layer] * CM_PER_M - - zwt_prime) / (temp.depth[layer] * CM_PER_M); - b = 0.5 * (temp.expt[layer] - 3); - bubble = temp.bubble[layer]; - tmp_resid_moist = temp.resid_moist[layer] * - temp.depth[layer] * MM_PER_M; + (tmp_depth2 * CM_PER_M + temp->depth[layer] * CM_PER_M - + zwt_prime) / (temp->depth[layer] * CM_PER_M); + b = 0.5 * (temp->expt[layer] - 3); + bubble = temp->bubble[layer]; + tmp_resid_moist = temp->resid_moist[layer] * + temp->depth[layer] * MM_PER_M; w_avg += -(b / (b - @@ -992,20 +994,20 @@ read_soilparam(FILE *soilparam, (1 - pow((zwt_prime + bubble - tmp_depth2 * CM_PER_M) / bubble, - (b - 1) / b)) / (temp.depth[layer] * CM_PER_M); + (b - 1) / b)) / (temp->depth[layer] * CM_PER_M); tmp_moist += w_avg * - (temp.max_moist[layer] - + (temp->max_moist[layer] - tmp_resid_moist) + tmp_resid_moist; b_save = b; bub_save = bubble; tmp_depth2_save = tmp_depth2; while (layer > 0) { layer--; - tmp_depth2 -= temp.depth[layer]; - b = 0.5 * (temp.expt[layer] - 3); - bubble = temp.bubble[layer]; - tmp_resid_moist = temp.resid_moist[layer] * - temp.depth[layer] * MM_PER_M; + tmp_depth2 -= temp->depth[layer]; + b = 0.5 * (temp->expt[layer] - 3); + bubble = temp->bubble[layer]; + tmp_resid_moist = temp->resid_moist[layer] * + temp->depth[layer] * MM_PER_M; zwt_prime_eff = tmp_depth2_save * CM_PER_M - bubble + bubble * pow( @@ -1018,24 +1020,24 @@ read_soilparam(FILE *soilparam, (1 - pow((zwt_prime_eff + bubble - tmp_depth2 * CM_PER_M) / bubble, - (b - 1) / b)) / (temp.depth[layer] * CM_PER_M); + (b - 1) / b)) / (temp->depth[layer] * CM_PER_M); tmp_moist += w_avg * - (temp.max_moist[layer] - + (temp->max_moist[layer] - tmp_resid_moist) + tmp_resid_moist; b_save = b; bub_save = bubble; tmp_depth2_save = tmp_depth2; } - temp.zwtvmoist_moist[options.Nlayer + 1][i] = tmp_moist; + temp->zwtvmoist_moist[options.Nlayer + 1][i] = tmp_moist; } zwt_prime += tmp_depth * CM_PER_M / (MAX_ZWTVMOIST - 1); // in cm } /* Compute soil albedo in PAR range (400-700nm) following eqn 122 in Knorr 1997 */ if (options.CARBON) { - temp.AlbedoPar = 0.92 * param.ALBEDO_BARE_SOIL - 0.015; - if (temp.AlbedoPar < param.PHOTO_ALBSOIPARMIN) { - temp.AlbedoPar = param.PHOTO_ALBSOIPARMIN; + temp->AlbedoPar = 0.92 * param.ALBEDO_BARE_SOIL - 0.015; + if (temp->AlbedoPar < param.PHOTO_ALBSOIPARMIN) { + temp->AlbedoPar = param.PHOTO_ALBSOIPARMIN; } } @@ -1043,13 +1045,11 @@ read_soilparam(FILE *soilparam, Miscellaneous terms for MTCLIM disaggregation *************************************************/ /* Central Longitude of Current Time Zone */ - temp.time_zone_lng = off_gmt * 360. / HOURS_PER_DAY; + temp->time_zone_lng = off_gmt * 360. / HOURS_PER_DAY; /* Assume flat grid cell for radiation calculations */ - temp.slope = 0; - temp.aspect = 0; - temp.whoriz = 0; - temp.ehoriz = 0; + temp->slope = 0; + temp->aspect = 0; + temp->whoriz = 0; + temp->ehoriz = 0; } // end if(!(*MODEL_DONE) && (*RUN_MODEL)) - - return temp; } diff --git a/vic/drivers/classic/src/vic_classic.c b/vic/drivers/classic/src/vic_classic.c index 65e46705c..b5ec546d6 100644 --- a/vic/drivers/classic/src/vic_classic.c +++ b/vic/drivers/classic/src/vic_classic.c @@ -58,8 +58,8 @@ main(int argc, /** Variable Declarations **/ extern FILE *LOG_DEST; - char MODEL_DONE; - char RUN_MODEL; + bool MODEL_DONE; + bool RUN_MODEL; char dmy_str[MAXSTRING]; size_t rec; size_t Nveg_type; @@ -68,7 +68,7 @@ main(int argc, int ErrorFlag; size_t streamnum; dmy_struct *dmy; - atmos_data_struct *atmos; + force_data_struct *force; veg_hist_struct **veg_hist; veg_con_struct *veg_con; soil_con_struct soil_con; @@ -77,6 +77,13 @@ main(int argc, stream_struct *streams = NULL; double ***out_data; // [1, nvars, nelem] save_data_struct save_data; + timer_struct global_timers[N_TIMERS]; + timer_struct cell_timer; + + // start vic all timer + timer_start(&(global_timers[TIMER_VIC_ALL])); + // start vic init timer + timer_start(&(global_timers[TIMER_VIC_INIT])); // Initialize Log Destination initialize_log(); @@ -99,7 +106,7 @@ main(int argc, fclose(filep.globalparam); // Set Log Destination - setup_logging(MISSING); + setup_logging(MISSING, filenames.log_path, &(filep.logfile)); /** Set model constants **/ if (strcmp(filenames.constants, "MISSING") != 0) { @@ -130,8 +137,8 @@ main(int argc, /** Initialize Parameters **/ cellnum = -1; - /** allocate memory for the atmos_data_struct **/ - alloc_atmos(global_param.nrecs, &atmos); + /** allocate memory for the force_data_struct **/ + alloc_atmos(global_param.nrecs, &force); /** Initial state **/ startrec = 0; @@ -155,8 +162,14 @@ main(int argc, Run Model for all Active Grid Cells ************************************/ MODEL_DONE = false; + + // stop init timer + timer_stop(&(global_timers[TIMER_VIC_INIT])); + // start vic run timer + timer_start(&(global_timers[TIMER_VIC_RUN])); + while (!MODEL_DONE) { - soil_con = read_soilparam(filep.soilparam, &RUN_MODEL, &MODEL_DONE); + read_soilparam(filep.soilparam, &soil_con, &RUN_MODEL, &MODEL_DONE); if (RUN_MODEL) { cellnum++; @@ -190,7 +203,7 @@ main(int argc, Have not Been Specifically Set **************************************************/ - vic_force(atmos, dmy, filep.forcing, veg_con, veg_hist, &soil_con); + vic_force(force, dmy, filep.forcing, veg_con, veg_hist, &soil_con); /************************************************** Initialize Energy Balance and Snow Variables @@ -199,12 +212,10 @@ main(int argc, vic_populate_model_state(&all_vars, filep, soil_con.gridcel, &soil_con, veg_con, lake_con); - /** Update Error Handling Structure **/ - Error.filep = filep; - /** Initialize the storage terms in the water and energy balances **/ - initialize_save_data(&all_vars, &atmos[0], &soil_con, veg_con, - veg_lib, &lake_con, out_data[0], &save_data); + initialize_save_data(&all_vars, &force[0], &soil_con, veg_con, + veg_lib, &lake_con, out_data[0], &save_data, + &cell_timer); /****************************************** Run Model in Grid Cell for all Time Steps @@ -226,15 +237,17 @@ main(int argc, /************************************************** Compute cell physics for 1 timestep **************************************************/ - ErrorFlag = vic_run(&atmos[rec], &all_vars, + timer_start(&cell_timer); + ErrorFlag = vic_run(&force[rec], &all_vars, &(dmy[rec]), &global_param, &lake_con, &soil_con, veg_con, veg_lib); + timer_stop(&cell_timer); /************************************************** Calculate cell average values for current time step **************************************************/ - put_data(&all_vars, &atmos[rec], &soil_con, veg_con, veg_lib, - &lake_con, out_data[0], &save_data); + put_data(&all_vars, &force[rec], &soil_con, veg_con, veg_lib, + &lake_con, out_data[0], &save_data, &cell_timer); for (streamnum = 0; streamnum < options.Noutstreams; @@ -264,7 +277,7 @@ main(int argc, "so the simulation has not finished. An " "incomplete output file has been " "generated, check your inputs before " - "rerunning the simulation.\n", + "rerunning the simulation.", soil_con.gridcel, rec); break; } @@ -272,7 +285,7 @@ main(int argc, // Else exit program on cell solution error as in previous versions log_err("ERROR: Grid cell %i failed in record %zu " "so the simulation has ended. Check your " - "inputs before rerunning the simulation.\n", + "inputs before rerunning the simulation.", soil_con.gridcel, rec); } } @@ -292,8 +305,13 @@ main(int argc, } /* End Run Model Condition */ } /* End Grid Loop */ + // stop vic run timer + timer_stop(&(global_timers[TIMER_VIC_RUN])); + // start vic final timer + timer_start(&(global_timers[TIMER_VIC_FINAL])); + /** cleanup **/ - free_atmos(global_param.nrecs, &atmos); + free_atmos(global_param.nrecs, &force); free_dmy(&dmy); free_streams(&streams); free_out_data(1, out_data); // 1 is for the number of gridcells, 1 in classic driver @@ -317,5 +335,12 @@ main(int argc, log_info("Completed running VIC %s", VIC_DRIVER); + // stop vic final timer + timer_stop(&(global_timers[TIMER_VIC_FINAL])); + // stop vic all timer + timer_stop(&(global_timers[TIMER_VIC_ALL])); + // write timing info + write_vic_timing_table(global_timers); + return EXIT_SUCCESS; } /* End Main Program */ diff --git a/vic/drivers/classic/src/vic_classic_timing.c b/vic/drivers/classic/src/vic_classic_timing.c new file mode 100644 index 000000000..275a06d4d --- /dev/null +++ b/vic/drivers/classic/src/vic_classic_timing.c @@ -0,0 +1,147 @@ +/****************************************************************************** + * @section DESCRIPTION + * + * Write vic timing table for classic driver + * + * @section LICENSE + * + * The Variable Infiltration Capacity (VIC) macroscale hydrological model + * Copyright (C) 2016 The Computational Hydrology Group, Department of Civil + * and Environmental Engineering, University of Washington. + * + * The VIC model 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 2 + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + *****************************************************************************/ + +#include + +/****************************************************************************** + * @brief VIC timing file + *****************************************************************************/ +void +write_vic_timing_table(timer_struct *timers) +{ + extern FILE *LOG_DEST; + extern filenames_struct filenames; + extern global_param_struct global_param; + + char machine[MAXSTRING]; + char user[MAXSTRING]; + time_t curr_date_time; + struct tm *timeinfo; + uid_t uid; + struct passwd *pw; + double ndays; + double nyears; + + // datestr + curr_date_time = time(NULL); + if (curr_date_time == -1) { + log_err("Failed to get the current time!"); + } + timeinfo = localtime(&curr_date_time); + + // hostname + if (gethostname(machine, MAXSTRING) != 0) { + strcpy(machine, "unknown"); + } + + // username + uid = geteuid(); + pw = getpwuid(uid); + + if (pw) { + strcpy(user, pw->pw_name); + } + else { + strcpy(user, "unknown"); + } + + // calculate run length + ndays = global_param.dt * global_param.nrecs / SEC_PER_DAY; + nyears = ndays / DAYS_PER_YEAR; + + fprintf(LOG_DEST, "\n"); + fprintf(LOG_DEST, + "------------------------------" + " VIC TIMING PROFILE " + "------------------------------\n\n"); + fprintf(LOG_DEST, " Date : %s", asctime(timeinfo)); + fprintf(LOG_DEST, " Compiler : %s (%s)\n", COMPILER, + COMPILER_VERSION); + fprintf(LOG_DEST, " Machine : %s\n", machine); + fprintf(LOG_DEST, " VIC User : %s\n", user); + fprintf(LOG_DEST, " VIC Version : %s\n", GIT_VERSION); + fprintf(LOG_DEST, " VIC GIT Version : %s\n", VERSION); + fprintf(LOG_DEST, " VIC_DRIVER : %s\n", VIC_DRIVER); + fprintf(LOG_DEST, "\n"); + fprintf(LOG_DEST, " Global Param File : %s\n", filenames.global); + fprintf(LOG_DEST, " Start Date : %04hu-%02hu-%02hu-%05u\n", + global_param.startyear, global_param.startmonth, + global_param.startday, global_param.startsec); + fprintf(LOG_DEST, " Stop Date : %04hu-%02hu-%02hu\n", + global_param.endyear, global_param.endmonth, global_param.endday); + fprintf(LOG_DEST, " Nrecs : %zu\n", + global_param.nrecs); + fprintf(LOG_DEST, " Model Timestep (seconds) : %g\n", global_param.dt); + fprintf(LOG_DEST, " Snow Timestep (seconds) : %g\n", + global_param.snow_dt); + fprintf(LOG_DEST, " Runoff Timestep (seconds) : %g\n", + global_param.runoff_dt); + fprintf(LOG_DEST, " Atmos Timestep (seconds) : %g\n", + global_param.atmos_dt); + fprintf(LOG_DEST, "\n"); + + fprintf(LOG_DEST, "\n"); + + fprintf(LOG_DEST, " Overall Metrics\n"); + fprintf(LOG_DEST, " ---------------\n"); + fprintf(LOG_DEST, " Model Cost : %g pe-hrs/simulated_year\n", + timers[TIMER_VIC_ALL].delta_wall / SEC_PER_HOUR / nyears); + fprintf(LOG_DEST, " Model Throughput : %g simulated_years/day\n", + nyears / (timers[TIMER_VIC_ALL].delta_wall / SEC_PER_DAY)); + fprintf(LOG_DEST, "\n"); + fprintf(LOG_DEST, " Timing Table:\n"); + fprintf(LOG_DEST, + "|------------|----------------------|----------------------|----------------------|----------------------|\n"); + fprintf(LOG_DEST, + "| Timer | Wall Time (secs) | CPU Time (secs) | Wall Time (secs/day) | CPU Time (secs/day) |\n"); + fprintf(LOG_DEST, + "|------------|----------------------|----------------------|----------------------|----------------------|\n"); + fprintf(LOG_DEST, "| Init Time | %20g | %20g | %20g | %20g |\n", + timers[TIMER_VIC_INIT].delta_wall, timers[TIMER_VIC_INIT].delta_cpu, + timers[TIMER_VIC_INIT].delta_wall / ndays, + timers[TIMER_VIC_INIT].delta_cpu / ndays); + fprintf(LOG_DEST, "| Run Time | %20g | %20g | %20g | %20g |\n", + timers[TIMER_VIC_RUN].delta_wall, timers[TIMER_VIC_RUN].delta_cpu, + timers[TIMER_VIC_RUN].delta_wall / ndays, + timers[TIMER_VIC_RUN].delta_cpu / ndays); + fprintf(LOG_DEST, "| Final Time | %20g | %20g | %20g | %20g |\n", + timers[TIMER_VIC_FINAL].delta_wall, + timers[TIMER_VIC_FINAL].delta_cpu, + timers[TIMER_VIC_FINAL].delta_wall / ndays, + timers[TIMER_VIC_FINAL].delta_cpu / ndays); + fprintf(LOG_DEST, "| Total Time | %20g | %20g | %20g | %20g |\n", + timers[TIMER_VIC_ALL].delta_wall, timers[TIMER_VIC_ALL].delta_cpu, + timers[TIMER_VIC_ALL].delta_wall / ndays, + timers[TIMER_VIC_ALL].delta_cpu / ndays); + fprintf(LOG_DEST, + "|------------|----------------------|----------------------|----------------------|----------------------|\n"); + fprintf(LOG_DEST, "\n"); + + fprintf(LOG_DEST, + "\n------------------------------" + " END VIC TIMING PROFILE " + "------------------------------\n\n"); +} diff --git a/vic/drivers/classic/src/vic_force.c b/vic/drivers/classic/src/vic_force.c index 99c374832..36c9f85a7 100644 --- a/vic/drivers/classic/src/vic_force.c +++ b/vic/drivers/classic/src/vic_force.c @@ -31,7 +31,7 @@ * @brief Initialize atmospheric variables for the model and snow time steps. *****************************************************************************/ void -vic_force(atmos_data_struct *atmos, +vic_force(force_data_struct *force, dmy_struct *dmy, FILE **infile, veg_con_struct *veg_con, @@ -125,48 +125,52 @@ vic_force(atmos_data_struct *atmos, for (i = 0; i < NF; i++) { uidx = rec * NF + i; // temperature in Celsius - atmos[rec].air_temp[i] = forcing_data[AIR_TEMP][uidx]; + force[rec].air_temp[i] = forcing_data[AIR_TEMP][uidx]; // precipitation in mm/period - atmos[rec].prec[i] = forcing_data[PREC][uidx]; + force[rec].prec[i] = forcing_data[PREC][uidx]; // downward shortwave in W/m2 - atmos[rec].shortwave[i] = forcing_data[SWDOWN][uidx]; + force[rec].shortwave[i] = forcing_data[SWDOWN][uidx]; // downward longwave in W/m2 - atmos[rec].longwave[i] = forcing_data[LWDOWN][uidx]; + force[rec].longwave[i] = forcing_data[LWDOWN][uidx]; // pressure in Pa - atmos[rec].pressure[i] = forcing_data[PRESSURE][uidx] * PA_PER_KPA; + force[rec].pressure[i] = forcing_data[PRESSURE][uidx] * PA_PER_KPA; // vapor pressure in Pa - atmos[rec].vp[i] = forcing_data[VP][uidx] * PA_PER_KPA; + force[rec].vp[i] = forcing_data[VP][uidx] * PA_PER_KPA; // vapor pressure deficit in Pa - atmos[rec].vpd[i] = svp(atmos[rec].air_temp[i]) - atmos[rec].vp[i]; + force[rec].vpd[i] = svp(force[rec].air_temp[i]) - force[rec].vp[i]; + if (force[rec].vpd[i] < 0) { + force[rec].vpd[i] = 0; + force[rec].vp[i] = svp(force[rec].air_temp[i]); + } // air density in kg/m3 - atmos[rec].density[i] = air_density(atmos[rec].air_temp[i], - atmos[rec].pressure[i]); + force[rec].density[i] = air_density(force[rec].air_temp[i], + force[rec].pressure[i]); // wind speed in m/s - atmos[rec].wind[i] = forcing_data[WIND][uidx]; + force[rec].wind[i] = forcing_data[WIND][uidx]; // snow flag - atmos[rec].snowflag[i] = will_it_snow(&(atmos[rec].air_temp[i]), + force[rec].snowflag[i] = will_it_snow(&(force[rec].air_temp[i]), t_offset, param.SNOW_MAX_SNOW_TEMP, - &(atmos[rec].prec[i]), 1); + &(force[rec].prec[i]), 1); // Optional inputs if (options.LAKES) { // Channel inflow from upstream (into lake) if (param_set.TYPE[CHANNEL_IN].SUPPLIED) { - atmos[rec].channel_in[i] = forcing_data[CHANNEL_IN][uidx]; + force[rec].channel_in[i] = forcing_data[CHANNEL_IN][uidx]; } else { - atmos[rec].channel_in[i] = 0; + force[rec].channel_in[i] = 0; } } if (options.CARBON) { // Atmospheric CO2 concentration - atmos[rec].Catm[i] = forcing_data[CATM][uidx]; + force[rec].Catm[i] = forcing_data[CATM][uidx]; // Fraction of shortwave that is direct - atmos[rec].fdir[i] = forcing_data[FDIR][uidx]; + force[rec].fdir[i] = forcing_data[FDIR][uidx]; // photosynthetically active radiation - atmos[rec].par[i] = forcing_data[PAR][uidx]; + force[rec].par[i] = forcing_data[PAR][uidx]; // Cosine of solar zenith angle - atmos[rec].coszen[i] = compute_coszen(soil_con->lat, + force[rec].coszen[i] = compute_coszen(soil_con->lat, soil_con->lng, soil_con->time_zone_lng, dmy[rec].day_in_year, @@ -174,32 +178,32 @@ vic_force(atmos_data_struct *atmos, } } if (NF > 1) { - atmos[rec].air_temp[NR] = average(atmos[rec].air_temp, NF); + force[rec].air_temp[NR] = average(force[rec].air_temp, NF); // For precipitation put total - atmos[rec].prec[NR] = average(atmos[rec].prec, NF) * NF; - atmos[rec].shortwave[NR] = average(atmos[rec].shortwave, NF); - atmos[rec].longwave[NR] = average(atmos[rec].longwave, NF); - atmos[rec].pressure[NR] = average(atmos[rec].pressure, NF); - atmos[rec].vp[NR] = average(atmos[rec].vp, NF); - atmos[rec].vpd[NR] = average(atmos[rec].vpd, NF); - atmos[rec].density[NR] = average(atmos[rec].density, NF); - atmos[rec].wind[NR] = average(atmos[rec].wind, NF); - atmos[rec].snowflag[NR] = false; + force[rec].prec[NR] = average(force[rec].prec, NF) * NF; + force[rec].shortwave[NR] = average(force[rec].shortwave, NF); + force[rec].longwave[NR] = average(force[rec].longwave, NF); + force[rec].pressure[NR] = average(force[rec].pressure, NF); + force[rec].vp[NR] = average(force[rec].vp, NF); + force[rec].vpd[NR] = average(force[rec].vpd, NF); + force[rec].density[NR] = average(force[rec].density, NF); + force[rec].wind[NR] = average(force[rec].wind, NF); + force[rec].snowflag[NR] = false; for (i = 0; i < NF; i++) { - if (atmos[rec].snowflag[i] == true) { - atmos[rec].snowflag[NR] = true; + if (force[rec].snowflag[i] == true) { + force[rec].snowflag[NR] = true; } } if (options.LAKES) { - atmos[rec].channel_in[NR] = - average(atmos[rec].channel_in, NF) * NF; + force[rec].channel_in[NR] = + average(force[rec].channel_in, NF) * NF; } if (options.CARBON) { - atmos[rec].Catm[NR] = average(atmos[rec].Catm, NF); - atmos[rec].fdir[NR] = average(atmos[rec].fdir, NF); - atmos[rec].par[NR] = average(atmos[rec].par, NF); + force[rec].Catm[NR] = average(force[rec].Catm, NF); + force[rec].fdir[NR] = average(force[rec].fdir, NF); + force[rec].par[NR] = average(force[rec].par, NF); // for coszen, use value at noon - atmos[rec].coszen[NR] = compute_coszen(soil_con->lat, + force[rec].coszen[NR] = compute_coszen(soil_con->lat, soil_con->lng, soil_con->time_zone_lng, dmy[rec].day_in_year, @@ -259,7 +263,7 @@ vic_force(atmos_data_struct *atmos, // Check on fcanopy if (veg_hist[rec][v].fcanopy[i] < MIN_FCANOPY) { log_warn( - "rec %zu, veg %zu substep %zu fcanopy %f < minimum of %f; setting = %f\n", rec, v, i, + "rec %zu, veg %zu substep %zu fcanopy %f < minimum of %f; setting = %f", rec, v, i, veg_hist[rec][v].fcanopy[i], MIN_FCANOPY, MIN_FCANOPY); veg_hist[rec][v].fcanopy[i] = MIN_FCANOPY; @@ -305,7 +309,7 @@ vic_force(atmos_data_struct *atmos, if (options.COMPUTE_TREELINE) { if (!(options.JULY_TAVG_SUPPLIED && avgJulyAirTemp == -999)) { - compute_treeline(atmos, dmy, avgJulyAirTemp, Tfactor, + compute_treeline(force, dmy, avgJulyAirTemp, Tfactor, AboveTreeLine); } } diff --git a/vic/drivers/classic/src/write_data.c b/vic/drivers/classic/src/write_data.c index 738e185bb..1dbdc3bf9 100644 --- a/vic/drivers/classic/src/write_data.c +++ b/vic/drivers/classic/src/write_data.c @@ -105,8 +105,8 @@ write_data(stream_struct *stream, elem_idx < out_metadata[varid].nelem; elem_idx++) { tmp_usiptr[ptr_idx++] = - (unsigned short int) stream->aggdata[var_idx][elem_idx][ - 0]; + (unsigned short int) stream->aggdata[0][var_idx][ + elem_idx][0]; } fwrite(tmp_usiptr, sizeof(unsigned short int), ptr_idx, stream->fh); diff --git a/vic/drivers/image/Makefile b/vic/drivers/image/Makefile index 103a8832a..b80455cd4 100644 --- a/vic/drivers/image/Makefile +++ b/vic/drivers/image/Makefile @@ -38,43 +38,23 @@ SHAREDIMAGEPATH = ../shared_image # VIC RUN PATH VICPATH = ../../vic_run -UNAME_S := $(shell uname -s) - -# NETCDF include .. change the path depending on travis or other location -ifeq (true, ${TRAVIS}) -NETCDFPATH = ${TRAVIS_NETCDFPATH} -else ifeq (hydro-c1, $(shell hostname)) -NETCDFPATH = /usr/local/netcdf-4.3.0+gcc-4.7.2 -else ifeq ($(UNAME_S), Linux) -NETCDFPATH = /usr -else -NETCDFPATH = /opt/local +ifndef NC_LIBS +NC_LIBS = $(shell nc-config --libs) endif +$(info $$NC_LIBS is [${NC_LIBS}]) -# MPI include .. change the path depending on travis or other location -ifeq (true, ${TRAVIS}) -MPIPATH = ${TRAVIS_MPIPATH} -else ifeq (hydro-c1, $(shell hostname)) -MPIPATH = /usr/local/netcdf-4.3.0+gcc-4.7.2 -else ifeq ($(UNAME_S), Linux) -MPIPATH = /usr -else -MPIPATH = /opt/local +ifndef NC_CFLAGS +NC_CFLAGS = $(shell nc-config --cflags) endif +$(info $$NC_CFLAGS is [${NC_CFLAGS}]) -# Set SHELL = your shell here -SHELL = /bin/bash - -# Set CC = your compiler here -ifeq (true, ${TRAVIS}) -CC = ${MPIPATH}/bin/mpicc -else ifeq ($(UNAME_S), Linux) -CC = /usr/lib64/mpich/bin/mpicc -else -CC = mpicc +ifndef MPICC +MPICC = mpicc endif +$(info $$MPICC is [${MPICC}]) GIT_VERSION := $(shell git describe --abbrev=4 --dirty --always --tags) +UNAME_S := $(shell uname -s) HOSTNAME := $(shell uname -n) # Set the log level @@ -91,11 +71,10 @@ LOG_LVL = 5 INCLUDES = -I ${DRIVERPATH}/include \ -I ${VICPATH}/include \ -I ${SHAREDPATH}/include \ - -I ${SHAREDIMAGEPATH}/include \ - -I ${NETCDFPATH}/include \ + -I ${SHAREDIMAGEPATH}/include # Uncomment to include debugging information -CFLAGS = ${INCLUDES} -ggdb -O0 -Wall -Wextra -std=c99 \ +CFLAGS = ${INCLUDES} ${NC_CFLAGS} -ggdb -O0 -Wall -Wextra -std=c99 \ -DLOG_LVL=$(LOG_LVL) \ -DGIT_VERSION=\"$(GIT_VERSION)\" \ -DUSERNAME=\"$(USER)\" \ @@ -106,7 +85,7 @@ ifeq (true, ${TRAVIS}) CFLAGS += -rdynamic -Wl,-export-dynamic endif -LIBRARY = -lm -L${NETCDFPATH}/lib -lnetcdf +LIBRARY = -lm ${NC_LIBS} COMPEXE = vic_image EXT = .exe @@ -148,7 +127,7 @@ clean:: \rm -rf ${COMPEXE}${EXT} ${COMPEXE}${EXT}.dSYM model: $(OBJS) - $(CC) -o ${COMPEXE}${EXT} $(OBJS) $(CFLAGS) $(LIBRARY) + $(MPICC) -o ${COMPEXE}${EXT} $(OBJS) $(CFLAGS) $(LIBRARY) # ------------------------------------------------------------- # tags @@ -166,7 +145,7 @@ clean:: # ------------------------------------------------------------- depend: .depend .depend: $(SRCS) $(HDRS) - $(CC) $(CFLAGS) -M $(SRCS) > $@ + $(MPICC) $(CFLAGS) -M $(SRCS) > $@ clean:: \rm -f .depend diff --git a/vic/drivers/image/src/display_current_settings.c b/vic/drivers/image/src/display_current_settings.c index e280ecc11..6df978b2e 100644 --- a/vic/drivers/image/src/display_current_settings.c +++ b/vic/drivers/image/src/display_current_settings.c @@ -269,8 +269,7 @@ display_current_settings(int mode) fprintf(LOG_DEST, "\n"); fprintf(LOG_DEST, "Constants File\t\t%s\n", filenames.constants); - fprintf(LOG_DEST, "Input Soil Data:\n"); - fprintf(LOG_DEST, "Soil file\t\t%s\n", filenames.soil); + fprintf(LOG_DEST, "Parameters file\t\t%s\n", filenames.params); if (options.BASEFLOW == ARNO) { fprintf(LOG_DEST, "BASEFLOW\t\tARNO\n"); } @@ -291,15 +290,12 @@ display_current_settings(int mode) } fprintf(LOG_DEST, "\n"); - fprintf(LOG_DEST, "Input Veg Data:\n"); - fprintf(LOG_DEST, "Veg library file\t%s\n", filenames.veglib); if (options.VEGLIB_PHOTO) { fprintf(LOG_DEST, "VEGLIB_PHOTO\t\tTRUE\n"); } else { fprintf(LOG_DEST, "VEGLIB_PHOTO\t\tFALSE\n"); } - fprintf(LOG_DEST, "Veg param file\t\t%s\n", filenames.veg); fprintf(LOG_DEST, "ROOT_ZONES\t\t%zu\n", options.ROOT_ZONES); if (options.VEGPARAM_LAI) { fprintf(LOG_DEST, "VEGPARAM_LAI\t\tTRUE\n"); @@ -353,8 +349,7 @@ display_current_settings(int mode) fprintf(LOG_DEST, "\n"); fprintf(LOG_DEST, "Input Elevation Data:\n"); if (options.SNOW_BAND > 1) { - fprintf(LOG_DEST, "SNOW_BAND\t\t%zu\t%s\n", options.SNOW_BAND, - filenames.snowband); + fprintf(LOG_DEST, "SNOW_BAND\t\t%zu\n", options.SNOW_BAND); } else if (options.SNOW_BAND == 1) { fprintf(LOG_DEST, @@ -368,7 +363,7 @@ display_current_settings(int mode) fprintf(LOG_DEST, "\n"); fprintf(LOG_DEST, "Input Lake Data:\n"); if (options.LAKES) { - fprintf(LOG_DEST, "LAKES\t\tTRUE\t%s\n", filenames.lakeparam); + fprintf(LOG_DEST, "LAKES\t\tTRUE\n"); } else { fprintf(LOG_DEST, "LAKES\t\tFALSE\n"); diff --git a/vic/drivers/image/src/get_global_param.c b/vic/drivers/image/src/get_global_param.c index b5a886ada..4d0680b89 100644 --- a/vic/drivers/image/src/get_global_param.c +++ b/vic/drivers/image/src/get_global_param.c @@ -379,16 +379,8 @@ get_global_param(FILE *gp) else if (strcasecmp("DOMAIN_TYPE", optstr) == 0) { get_domain_type(cmdstr); } - else if (strcasecmp("SOIL", optstr) == 0) { - sscanf(cmdstr, "%*s %s", filenames.soil); - } - else if (strcasecmp("ARC_SOIL", optstr) == 0) { - sscanf(cmdstr, "%*s %s", flgstr); - if (strcasecmp("TRUE", flgstr) == 0) { - log_err("\"ARC_SOIL\" is no longer a supported option.\n" - "Please convert your soil parameter file and " - "remove this option from your global file."); - } + else if (strcasecmp("PARAMETERS", optstr) == 0) { + sscanf(cmdstr, "%*s %s", filenames.params); } else if (strcasecmp("ARNO_PARAMS", optstr) == 0) { sscanf(cmdstr, "%*s %s", flgstr); @@ -433,16 +425,10 @@ get_global_param(FILE *gp) sscanf(cmdstr, "%*s %s", flgstr); options.ORGANIC_FRACT = str_to_bool(flgstr); } - else if (strcasecmp("VEGLIB", optstr) == 0) { - sscanf(cmdstr, "%*s %s", filenames.veglib); - } else if (strcasecmp("VEGLIB_PHOTO", optstr) == 0) { sscanf(cmdstr, "%*s %s", flgstr); options.VEGLIB_PHOTO = str_to_bool(flgstr); } - else if (strcasecmp("VEGPARAM", optstr) == 0) { - sscanf(cmdstr, "%*s %s", filenames.veg); - } else if (strcasecmp("LAI_SRC", optstr) == 0) { sscanf(cmdstr, "%*s %s", flgstr); if (strcasecmp("FROM_VEGHIST", flgstr) == 0) { @@ -494,22 +480,15 @@ get_global_param(FILE *gp) "control file."); } } - else if (strcasecmp("ROOT_ZONES", optstr) == 0) { - sscanf(cmdstr, "%*s %zu", &options.ROOT_ZONES); - } else if (strcasecmp("SNOW_BAND", optstr) == 0) { - sscanf(cmdstr, "%*s %zu %s", &options.SNOW_BAND, - filenames.snowband); + sscanf(cmdstr, "%*s %s", flgstr); + if (str_to_bool(flgstr)) { + options.SNOW_BAND = SNOW_BAND_TRUE_BUT_UNSET; + } } else if (strcasecmp("LAKES", optstr) == 0) { sscanf(cmdstr, "%*s %s", flgstr); - if (strcasecmp("FALSE", flgstr) == 0) { - options.LAKES = false; - } - else { - options.LAKES = true; - strcpy(filenames.lakeparam, flgstr); - } + options.LAKES = str_to_bool(flgstr); } else if (strcasecmp("LAKE_PROFILE", optstr) == 0) { sscanf(cmdstr, "%*s %s", flgstr); @@ -846,27 +825,11 @@ get_global_param(FILE *gp) "begins with \"RESULT_DIR\"."); } - // Validate soil parameter file information - if (strcmp(filenames.soil, "MISSING") == 0) { - log_err("No soil parameter file has been defined. Make sure that the " - "global file defines the soil parameter file on the line that " - "begins with \"SOIL\"."); - } - - // Validate veg parameter information - if (strcmp(filenames.veg, "MISSING") == 0) { - log_err("No vegetation parameter file has been defined. Make sure " - "that the global file defines the vegetation parameter " - "file on the line that begins with \"VEGPARAM\"."); - } - if (strcmp(filenames.veglib, "MISSING") == 0) { - log_err("No vegetation library file has been defined. Make sure " - "that the global file defines the vegetation library file " - "on the line that begins with \"VEGLIB\"."); - } - if (options.ROOT_ZONES == 0) { - log_err("ROOT_ZONES must be defined to a positive integer greater " - "than 0, in the global control file."); + // Validate parameter file information + if (strcmp(filenames.params, "MISSING") == 0) { + log_err("A parameters file has not been defined. Make sure that the " + "global file defines the parameters parameter file on the line " + "that begins with \"PARAMETERS\"."); } // Validate SPATIAL_FROST information @@ -900,27 +863,6 @@ get_global_param(FILE *gp) } } - // Validate the elevation band file information - if (options.SNOW_BAND > 1) { - if (strcmp(filenames.snowband, "MISSING") == 0) { - log_err("\"SNOW_BAND\" was specified with %zu elevation bands, " - "but no elevation band file has been defined. " - "Make sure that the global file defines the elevation " - "band file on the line that begins with \"SNOW_BAND\" " - "(after the number of bands).", options.SNOW_BAND); - } - if (options.SNOW_BAND > MAX_BANDS) { - log_err("Global file wants more snow bands (%zu) than are " - "defined by MAX_BANDS (%d). Edit vic_driver_shared.h and " - "recompile.", options.SNOW_BAND, MAX_BANDS); - } - } - else if (options.SNOW_BAND <= 0) { - log_err("Invalid number of elevation bands specified in global " - "file (%zu). Number of bands must be >= 1.", - options.SNOW_BAND); - } - // Validate the input state file information if (options.INIT_STATE) { if (strcmp(filenames.init_state, "MISSING") == 0) { @@ -1040,12 +982,6 @@ get_global_param(FILE *gp) log_err("FULL_ENERGY must be TRUE if the lake model is to " "be run."); } - if (strcmp(filenames.lakeparam, "MISSING") == 0) { - log_err("\"LAKES\" was specified, but no lake parameter " - "file has been defined. Make sure that the global " - "file defines the lake parameter file on the line that " - "begins with \"LAKES\"."); - } if (options.COMPUTE_TREELINE) { log_err("LAKES = TRUE and COMPUTE_TREELINE = TRUE are " "incompatible options."); diff --git a/vic/drivers/image/src/vic_force.c b/vic/drivers/image/src/vic_force.c index 5b34f60e4..c52fd1a84 100644 --- a/vic/drivers/image/src/vic_force.c +++ b/vic/drivers/image/src/vic_force.c @@ -35,7 +35,7 @@ vic_force(void) extern size_t NF; extern size_t NR; extern size_t current; - extern atmos_data_struct *atmos; + extern force_data_struct *force; extern dmy_struct *dmy; extern domain_struct global_domain; extern domain_struct local_domain; @@ -91,7 +91,7 @@ vic_force(void) param_set.TYPE[AIR_TEMP].varname, d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { - atmos[i].air_temp[j] = (double) dvar[i]; + force[i].air_temp[j] = (double) dvar[i]; } } @@ -103,7 +103,7 @@ vic_force(void) param_set.TYPE[PREC].varname, d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { - atmos[i].prec[j] = (double) dvar[i]; + force[i].prec[j] = (double) dvar[i]; } } @@ -115,7 +115,7 @@ vic_force(void) param_set.TYPE[SWDOWN].varname, d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { - atmos[i].shortwave[j] = (double) dvar[i]; + force[i].shortwave[j] = (double) dvar[i]; } } @@ -127,7 +127,7 @@ vic_force(void) param_set.TYPE[LWDOWN].varname, d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { - atmos[i].longwave[j] = (double) dvar[i]; + force[i].longwave[j] = (double) dvar[i]; } } @@ -139,11 +139,11 @@ vic_force(void) param_set.TYPE[WIND].varname, d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { - atmos[i].wind[j] = (double) dvar[i]; + force[i].wind[j] = (double) dvar[i]; } } - // Specific humidity: shum + // vapor pressure: vp for (j = 0; j < NF; j++) { d3start[0] = global_param.forceskip[0] + global_param.forceoffset[0] + j; @@ -151,7 +151,7 @@ vic_force(void) param_set.TYPE[VP].varname, d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { - atmos[i].vp[j] = (double) dvar[i]; + force[i].vp[j] = (double) dvar[i]; } } @@ -163,7 +163,7 @@ vic_force(void) param_set.TYPE[PRESSURE].varname, d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { - atmos[i].pressure[j] = (double) dvar[i]; + force[i].pressure[j] = (double) dvar[i]; } } // Optional inputs @@ -176,7 +176,7 @@ vic_force(void) d3start, d3count, dvar); for (j = 0; j < NF; j++) { for (i = 0; i < local_domain.ncells_active; i++) { - atmos[i].channel_in[j] = (double) dvar[i]; + force[i].channel_in[j] = (double) dvar[i]; } } } @@ -189,13 +189,13 @@ vic_force(void) param_set.TYPE[CATM].varname, d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { - atmos[i].Catm[j] = (double) dvar[i]; + force[i].Catm[j] = (double) dvar[i]; } } // Cosine of solar zenith angle for (j = 0; j < NF; j++) { for (i = 0; i < local_domain.ncells_active; i++) { - atmos[i].coszen[j] = compute_coszen( + force[i].coszen[j] = compute_coszen( local_domain.locations[i].latitude, local_domain.locations[i].longitude, soil_con[i].time_zone_lng, dmy[current].day_in_year, @@ -210,7 +210,7 @@ vic_force(void) param_set.TYPE[FDIR].varname, d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { - atmos[i].fdir[j] = (double) dvar[i]; + force[i].fdir[j] = (double) dvar[i]; } } // Photosynthetically active radiation @@ -221,7 +221,7 @@ vic_force(void) param_set.TYPE[PAR].varname, d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { - atmos[i].par[j] = (double) dvar[i]; + force[i].par[j] = (double) dvar[i]; } } } @@ -358,22 +358,25 @@ vic_force(void) for (i = 0; i < local_domain.ncells_active; i++) { for (j = 0; j < NF; j++) { // temperature in Celsius - atmos[i].air_temp[j] -= CONST_TKFRZ; + force[i].air_temp[j] -= CONST_TKFRZ; // precipitation in mm/period - atmos[i].prec[j] *= global_param.snow_dt; - // pressure in Pa - // vapor pressure in Pa (we read specific humidity in kg/kg) - atmos[i].vp[j] = q_to_vp(atmos[i].vp[j], atmos[i].pressure[j]); + force[i].prec[j] *= global_param.snow_dt; + // vapor pressure in Pa + force[i].vp[j] *= PA_PER_KPA; // vapor pressure deficit in Pa - atmos[i].vpd[j] = svp(atmos[i].air_temp[j]) - atmos[i].vp[j]; + force[i].vpd[j] = svp(force[i].air_temp[j]) - force[i].vp[j]; + if (force[i].vpd[j] < 0) { + force[i].vpd[j] = 0; + force[i].vp[j] = svp(force[i].air_temp[j]); + } // air density in kg/m3 - atmos[i].density[j] = air_density(atmos[i].air_temp[j], - atmos[i].pressure[j]); + force[i].density[j] = air_density(force[i].air_temp[j], + force[i].pressure[j]); // snow flag - atmos[i].snowflag[j] = will_it_snow(&(atmos[i].air_temp[j]), + force[i].snowflag[j] = will_it_snow(&(force[i].air_temp[j]), t_offset[i], param.SNOW_MAX_SNOW_TEMP, - &(atmos[i].prec[j]), 1); + &(force[i].prec[j]), 1); } // Check on fcanopy for (v = 0; v < options.NVEGTYPES; v++) { @@ -385,7 +388,7 @@ vic_force(void) (options.FCAN_SRC == FROM_VEGHIST))) { // Only issue this warning once if not using veg hist fractions log_warn( - "cell %zu, veg` %d substep %zu fcanopy %f < minimum of %f; setting = %f\n", i, vidx, j, + "cell %zu, veg` %d substep %zu fcanopy %f < minimum of %f; setting = %f", i, vidx, j, veg_hist[i][vidx].fcanopy[j], MIN_FCANOPY, MIN_FCANOPY); veg_hist[i][vidx].fcanopy[j] = MIN_FCANOPY; @@ -398,20 +401,20 @@ vic_force(void) // Put average value in NR field for (i = 0; i < local_domain.ncells_active; i++) { - atmos[i].air_temp[NR] = average(atmos[i].air_temp, NF); + force[i].air_temp[NR] = average(force[i].air_temp, NF); // For precipitation put total - atmos[i].prec[NR] = average(atmos[i].prec, NF) * NF; - atmos[i].shortwave[NR] = average(atmos[i].shortwave, NF); - atmos[i].longwave[NR] = average(atmos[i].longwave, NF); - atmos[i].pressure[NR] = average(atmos[i].pressure, NF); - atmos[i].wind[NR] = average(atmos[i].wind, NF); - atmos[i].vp[NR] = average(atmos[i].vp, NF); - atmos[i].vpd[NR] = (svp(atmos[i].air_temp[NR]) - atmos[i].vp[NR]); - atmos[i].density[NR] = air_density(atmos[i].air_temp[NR], - atmos[i].pressure[NR]); - atmos[i].snowflag[NR] = will_it_snow(atmos[i].air_temp, t_offset[i], + force[i].prec[NR] = average(force[i].prec, NF) * NF; + force[i].shortwave[NR] = average(force[i].shortwave, NF); + force[i].longwave[NR] = average(force[i].longwave, NF); + force[i].pressure[NR] = average(force[i].pressure, NF); + force[i].wind[NR] = average(force[i].wind, NF); + force[i].vp[NR] = average(force[i].vp, NF); + force[i].vpd[NR] = (svp(force[i].air_temp[NR]) - force[i].vp[NR]); + force[i].density[NR] = air_density(force[i].air_temp[NR], + force[i].pressure[NR]); + force[i].snowflag[NR] = will_it_snow(force[i].air_temp, t_offset[i], param.SNOW_MAX_SNOW_TEMP, - atmos[i].prec, NF); + force[i].prec, NF); for (v = 0; v < options.NVEGTYPES; v++) { vidx = veg_con_map[i].vidx[v]; @@ -433,14 +436,14 @@ vic_force(void) // Optional inputs if (options.LAKES) { - atmos[i].channel_in[NR] = average(atmos[i].channel_in, NF) * NF; + force[i].channel_in[NR] = average(force[i].channel_in, NF) * NF; } if (options.CARBON) { - atmos[i].Catm[NR] = average(atmos[i].Catm, NF); - atmos[i].fdir[NR] = average(atmos[i].fdir, NF); - atmos[i].par[NR] = average(atmos[i].par, NF); + force[i].Catm[NR] = average(force[i].Catm, NF); + force[i].fdir[NR] = average(force[i].fdir, NF); + force[i].par[NR] = average(force[i].par, NF); // for coszen, use value at noon - atmos[i].coszen[NR] = compute_coszen( + force[i].coszen[NR] = compute_coszen( local_domain.locations[i].latitude, local_domain.locations[i].longitude, soil_con[i].time_zone_lng, dmy[current].day_in_year, SEC_PER_DAY / 2); diff --git a/vic/drivers/image/src/vic_image.c b/vic/drivers/image/src/vic_image.c index b73d326f7..9ad7c04ba 100644 --- a/vic/drivers/image/src/vic_image.c +++ b/vic/drivers/image/src/vic_image.c @@ -31,7 +31,7 @@ size_t current; size_t *filter_active_cells = NULL; size_t *mpi_map_mapping_array = NULL; all_vars_struct *all_vars = NULL; -atmos_data_struct *atmos = NULL; +force_data_struct *force = NULL; dmy_struct *dmy = NULL; filenames_struct filenames; filep_struct filep; @@ -77,7 +77,14 @@ int main(int argc, char **argv) { - int status; + int status; + timer_struct global_timers[N_TIMERS]; + char state_filename[MAXSTRING]; + + // start vic all timer + timer_start(&(global_timers[TIMER_VIC_ALL])); + // start vic init timer + timer_start(&(global_timers[TIMER_VIC_INIT])); // Initialize MPI - note: logging not yet initialized status = MPI_Init(&argc, &argv); @@ -112,6 +119,17 @@ main(int argc, // initialize output structures vic_init_output(&(dmy[0])); + // Initialization is complete, print settings + log_info( + "Initialization is complete, print global param and options structures"); + print_global_param(&global_param); + print_option(&options); + + // stop init timer + timer_stop(&(global_timers[TIMER_VIC_INIT])); + // start vic run timer + timer_start(&(global_timers[TIMER_VIC_RUN])); + // loop over all timesteps for (current = 0; current < global_param.nrecs; current++) { // read forcing data @@ -125,20 +143,35 @@ main(int argc, // Write state file if (check_save_state_flag(current)) { - vic_store(&(dmy[current])); + debug("writing state file for timestep %zu", current); + vic_store(&(dmy[current]), state_filename); + debug("finished storing state file: %s", state_filename) } } - + // stop vic run timer + timer_stop(&(global_timers[TIMER_VIC_RUN])); + // start vic final timer + timer_start(&(global_timers[TIMER_VIC_FINAL])); // clean up vic_image_finalize(); // finalize MPI status = MPI_Finalize(); if (status != MPI_SUCCESS) { - log_err("MPI error in main(): %d\n", status); + log_err("MPI error: %d", status); } log_info("Completed running VIC %s", VIC_DRIVER); + // stop vic final timer + timer_stop(&(global_timers[TIMER_VIC_FINAL])); + // stop vic all timer + timer_stop(&(global_timers[TIMER_VIC_ALL])); + + if (mpi_rank == VIC_MPI_ROOT) { + // write timing info + write_vic_timing_table(global_timers, VIC_DRIVER); + } + return EXIT_SUCCESS; } diff --git a/vic/drivers/python/setup.py b/vic/drivers/python/setup.py index c3cbf1ceb..73425b38a 100644 --- a/vic/drivers/python/setup.py +++ b/vic/drivers/python/setup.py @@ -28,7 +28,7 @@ MICRO = 0 ISRELEASED = True VERSION = '%d.%d.%d' % (MAJOR, MINOR, MICRO) -QUALIFIER = 'rc1' +QUALIFIER = 'rc2' FULLVERSION = VERSION write_version = False diff --git a/vic/drivers/python/src/display_current_settings.c b/vic/drivers/python/src/display_current_settings.c index 0514149b3..305ea4892 100644 --- a/vic/drivers/python/src/display_current_settings.c +++ b/vic/drivers/python/src/display_current_settings.c @@ -37,10 +37,6 @@ display_current_settings(int mode) extern option_struct options; extern param_set_struct param_set; extern global_param_struct global_param; - extern filenames_struct filenames; - - int file_num; - print_version(VIC_DRIVER); @@ -242,34 +238,9 @@ display_current_settings(int mode) fprintf(LOG_DEST, "Ncanopy\t\t%zu\n", options.Ncanopy); fprintf(LOG_DEST, "\n"); - fprintf(LOG_DEST, "Input Forcing Data:\n"); - for (file_num = 0; file_num < 2; file_num++) { - if (global_param.forceyear[file_num] > 0) { - fprintf(LOG_DEST, "Forcing File %d:\t\t%s*\n", file_num + 1, - filenames.f_path_pfx[file_num]); - fprintf(LOG_DEST, "FORCEYEAR\t\t%d\n", - global_param.forceyear[file_num]); - fprintf(LOG_DEST, "FORCEMONTH\t\t%d\n", - global_param.forcemonth[file_num]); - fprintf(LOG_DEST, "FORCEDAY\t\t%d\n", - global_param.forceday[file_num]); - fprintf(LOG_DEST, "FORCESEC\t\t%d\n", - global_param.forcesec[file_num]); - fprintf(LOG_DEST, "N_TYPES\t\t\t%zu\n", - param_set.N_TYPES[file_num]); - fprintf(LOG_DEST, "FORCE_DT\t\t%f\n", param_set.FORCE_DT[file_num]); - } - } fprintf(LOG_DEST, "GRID_DECIMAL\t\t%hu\n", options.GRID_DECIMAL); fprintf(LOG_DEST, "\n"); - fprintf(LOG_DEST, "Input Domain Data:\n"); - fprintf(LOG_DEST, "Domain file\t\t%s\n", filenames.domain); - - fprintf(LOG_DEST, "\n"); - fprintf(LOG_DEST, "Constants File\t\t%s\n", filenames.constants); - fprintf(LOG_DEST, "Input Soil Data:\n"); - fprintf(LOG_DEST, "Soil file\t\t%s\n", filenames.soil); if (options.BASEFLOW == ARNO) { fprintf(LOG_DEST, "BASEFLOW\t\tARNO\n"); } @@ -290,15 +261,12 @@ display_current_settings(int mode) } fprintf(LOG_DEST, "\n"); - fprintf(LOG_DEST, "Input Veg Data:\n"); - fprintf(LOG_DEST, "Veg library file\t%s\n", filenames.veglib); if (options.VEGLIB_PHOTO) { fprintf(LOG_DEST, "VEGLIB_PHOTO\t\tTRUE\n"); } else { fprintf(LOG_DEST, "VEGLIB_PHOTO\t\tFALSE\n"); } - fprintf(LOG_DEST, "Veg param file\t\t%s\n", filenames.veg); fprintf(LOG_DEST, "ROOT_ZONES\t\t%zu\n", options.ROOT_ZONES); if (options.VEGPARAM_LAI) { fprintf(LOG_DEST, "VEGPARAM_LAI\t\tTRUE\n"); @@ -352,8 +320,7 @@ display_current_settings(int mode) fprintf(LOG_DEST, "\n"); fprintf(LOG_DEST, "Input Elevation Data:\n"); if (options.SNOW_BAND > 1) { - fprintf(LOG_DEST, "SNOW_BAND\t\t%zu\t%s\n", options.SNOW_BAND, - filenames.snowband); + fprintf(LOG_DEST, "SNOW_BAND\t\t%zu\n", options.SNOW_BAND); } else if (options.SNOW_BAND == 1) { fprintf(LOG_DEST, @@ -367,7 +334,7 @@ display_current_settings(int mode) fprintf(LOG_DEST, "\n"); fprintf(LOG_DEST, "Input Lake Data:\n"); if (options.LAKES) { - fprintf(LOG_DEST, "LAKES\t\tTRUE\t%s\n", filenames.lakeparam); + fprintf(LOG_DEST, "LAKES\t\tTRUE\n"); } else { fprintf(LOG_DEST, "LAKES\t\tFALSE\n"); @@ -382,7 +349,7 @@ display_current_settings(int mode) fprintf(LOG_DEST, "\n"); fprintf(LOG_DEST, "Input State File:\n"); if (options.INIT_STATE) { - fprintf(LOG_DEST, "INIT_STATE\t\tTRUE\t%s\n", filenames.init_state); + fprintf(LOG_DEST, "INIT_STATE\t\tTRUE\n"); if (options.STATE_FORMAT == BINARY) { fprintf(LOG_DEST, "STATE_FORMAT\tBINARY\n"); } @@ -398,7 +365,6 @@ display_current_settings(int mode) fprintf(LOG_DEST, "Output State File:\n"); if (options.SAVE_STATE) { fprintf(LOG_DEST, "SAVE_STATE\t\tTRUE\n"); - fprintf(LOG_DEST, "STATENAME\t\t%s\n", filenames.statefile); fprintf(LOG_DEST, "STATEYEAR\t\t%d\n", global_param.stateyear); fprintf(LOG_DEST, "STATEMONTH\t\t%d\n", global_param.statemonth); fprintf(LOG_DEST, "STATEDAY\t\t%d\n", global_param.stateday); @@ -415,6 +381,5 @@ display_current_settings(int mode) fprintf(LOG_DEST, "\n"); fprintf(LOG_DEST, "Output Data:\n"); - fprintf(LOG_DEST, "Result dir:\t\t%s\n", filenames.result_dir); fprintf(LOG_DEST, "\n"); } diff --git a/vic/drivers/python/src/globals.c b/vic/drivers/python/src/globals.c index 76f5ae5cf..ebd62c189 100644 --- a/vic/drivers/python/src/globals.c +++ b/vic/drivers/python/src/globals.c @@ -37,7 +37,5 @@ size_t NF; /* array index loop counter limit for atmos global_param_struct global_param; option_struct options; parameters_struct param; -filenames_struct filenames; -filep_struct filep; param_set_struct param_set; metadata_struct out_metadata[N_OUTVAR_TYPES]; diff --git a/vic/drivers/python/vic/vic.py b/vic/drivers/python/vic/vic.py index 04f9f6c48..0a610ca05 100644 --- a/vic/drivers/python/vic/vic.py +++ b/vic/drivers/python/vic/vic.py @@ -44,8 +44,6 @@ def _load_lib(lib): lib.initialize_global() lib.initialize_options() lib.initialize_parameters() -lib.initialize_filenames() -lib.initialize_fileps() # TODO: wrappers for individual vic functions. For now, access to lib functions # is made directly through the lib object diff --git a/vic/drivers/shared_all/include/vic_driver_shared_all.h b/vic/drivers/shared_all/include/vic_driver_shared_all.h index efdfb71e2..c4a98fa94 100644 --- a/vic/drivers/shared_all/include/vic_driver_shared_all.h +++ b/vic/drivers/shared_all/include/vic_driver_shared_all.h @@ -42,6 +42,8 @@ #define OUT_MULT_DEFAULT 0 // Why is this not 1? #define OUT_ASCII_FORMAT_DEFAULT "%.4f" +// Default snow band setting +#define SNOW_BAND_TRUE_BUT_UNSET 99999 /****************************************************************************** * @brief File formats @@ -277,6 +279,9 @@ enum OUT_CLITTER, /**< Carbon density in litter pool [g C/m2] */ OUT_CINTER, /**< Carbon density in intermediate pool [g C/m2] */ OUT_CSLOW, /**< Carbon density in slow pool [g C/m2] */ + // Timing and Profiling Terms + OUT_TIME_VICRUN_WALL, /**< Wall time spent inside vic_run [seconds] */ + OUT_TIME_VICRUN_CPU, /**< Wall time spent inside vic_run [seconds] */ // Last value of enum - DO NOT ADD ANYTHING BELOW THIS LINE!! // used as a loop counter and must be >= the largest value in this enum N_OUTVAR_TYPES /**< used as a loop counter*/ @@ -438,42 +443,16 @@ enum time_units }; /****************************************************************************** - * @brief file structures + * @brief Codes for timers *****************************************************************************/ -typedef struct { - FILE *forcing[MAX_FORCE_FILES]; /**< atmospheric forcing data files */ - FILE *globalparam; /**< global parameters file */ - FILE *constants; /**< model constants parameter file */ - FILE *domain; /**< domain file */ - FILE *init_state; /**< initial model state file */ - FILE *lakeparam; /**< lake parameter file */ - FILE *snowband; /**< snow elevation band data file */ - FILE *soilparam; /**< soil parameters for all grid cells */ - FILE *statefile; /**< output model state file */ - FILE *veglib; /**< vegetation parameters for all vege types */ - FILE *vegparam; /**< fractional coverage info for grid cell */ - FILE *logfile; /**< log file */ -} filep_struct; - -/****************************************************************************** - * @brief This structure stores input and output filenames. - *****************************************************************************/ -typedef struct { - char forcing[MAX_FORCE_FILES][MAXSTRING]; /**< atmospheric forcing data file names */ - char f_path_pfx[MAX_FORCE_FILES][MAXSTRING]; /**< path and prefix for atmospheric forcing data file names */ - char global[MAXSTRING]; /**< global control file name */ - char domain[MAXSTRING]; /**< domain file name */ - char constants[MAXSTRING]; /**< model constants file name */ - char init_state[MAXSTRING]; /**< initial model state file name */ - char lakeparam[MAXSTRING]; /**< lake model constants file */ - char result_dir[MAXSTRING]; /**< directory where results will be written */ - char snowband[MAXSTRING]; /**< snow band parameter file name */ - char soil[MAXSTRING]; /**< soil parameter file name */ - char statefile[MAXSTRING]; /**< name of file in which to store model state */ - char veg[MAXSTRING]; /**< vegetation grid coverage file */ - char veglib[MAXSTRING]; /**< vegetation parameter library file */ - char log_path[MAXSTRING]; /**< Location to write log file to*/ -} filenames_struct; +enum timers +{ + TIMER_VIC_ALL, + TIMER_VIC_INIT, + TIMER_VIC_RUN, + TIMER_VIC_FINAL, + N_TIMERS +}; /****************************************************************************** * @brief Stores forcing file input information. @@ -572,10 +551,9 @@ typedef struct { * routines. *****************************************************************************/ typedef struct { - atmos_data_struct *atmos; + force_data_struct *force; double dt; energy_bal_struct *energy; - filep_struct filep; size_t rec; double **out_data; stream_struct *output_streams; @@ -585,6 +563,18 @@ typedef struct { veg_var_struct *veg_var; } Error_struct; +/****************************************************************************** + * @brief This structure holds timer information for profiling + *****************************************************************************/ +typedef struct { + double start_wall; + double start_cpu; + double stop_wall; + double stop_cpu; + double delta_wall; + double delta_cpu; +} timer_struct; + double air_density(double t, double p); void agg_stream_data(stream_struct *stream, dmy_struct *dmy_current, double ***out_data); @@ -603,18 +593,20 @@ void collect_eb_terms(energy_bal_struct, snow_data_struct, cell_data_struct, double *, double, double **); void collect_wb_terms(cell_data_struct, veg_var_struct, snow_data_struct, double, double, double, bool, double, bool, double *, - double *, double **); + double **); void compute_derived_state_vars(all_vars_struct *, soil_con_struct *, veg_con_struct *); void compute_lake_params(lake_con_struct *, soil_con_struct); -void compute_treeline(atmos_data_struct *, dmy_struct *, double, double *, +void compute_treeline(force_data_struct *, dmy_struct *, double, double *, bool *); size_t count_force_vars(FILE *gp); void count_nstreams_nvars(FILE *gp, size_t *nstreams, size_t nvars[]); void cmd_proc(int argc, char **argv, char *globalfilename); void compress_files(char string[], short int level); stream_struct create_outstream(stream_struct *output_streams); +double get_cpu_time(); void get_current_datetime(char *cdt); +double get_wall_time(); double date2num(double origin, dmy_struct *date, double tzoffset, unsigned short int calendar, unsigned short int time_units); void dmy_all_30_day(double julian, dmy_struct *dmy); @@ -641,15 +633,14 @@ void get_parameters(FILE *paramfile); void init_output_list(double **out_data, int write, char *format, int type, double mult); void initialize_energy(energy_bal_struct **energy, size_t nveg); -void initialize_filenames(void); -void initialize_fileps(void); void initialize_global(void); void initialize_options(void); void initialize_parameters(void); -void initialize_save_data(all_vars_struct *all_vars, atmos_data_struct *atmos, +void initialize_save_data(all_vars_struct *all_vars, force_data_struct *force, soil_con_struct *soil_con, veg_con_struct *veg_con, veg_lib_struct *veg_lib, lake_con_struct *lake_con, - double **out_data, save_data_struct *save_data); + double **out_data, save_data_struct *save_data, + timer_struct *timer); void initialize_snow(snow_data_struct **snow, size_t veg_num); void initialize_soil(cell_data_struct **cell, size_t veg_num); void initialize_time(void); @@ -671,15 +662,13 @@ void num2date(double origin, double time_value, double tzoffset, FILE *open_file(char string[], char type[]); void parse_nc_time_units(char *nc_unit_chars, unsigned short int *units, dmy_struct *dmy); -void put_data(all_vars_struct *, atmos_data_struct *, soil_con_struct *, +void put_data(all_vars_struct *, force_data_struct *, soil_con_struct *, veg_con_struct *, veg_lib_struct *veg_lib, lake_con_struct *, - double **out_data, save_data_struct *); + double **out_data, save_data_struct *, timer_struct *timer); void print_alarm(alarm_struct *alarm); void print_cell_data(cell_data_struct *cell, size_t nlayers, size_t nfrost); void print_dmy(dmy_struct *dmy); void print_energy_bal(energy_bal_struct *eb, size_t nnodes, size_t nfronts); -void print_filenames(filenames_struct *fnames); -void print_filep(filep_struct *fp); void print_force_type(force_type_struct *force_type); void print_global_param(global_param_struct *gp); void print_lake_con(lake_con_struct *lcon, size_t nlnodes); @@ -734,6 +723,10 @@ unsigned short int str_to_out_type(char typestr[]); unsigned short int str_to_timeunits(char units_chars[]); void strpdmy(const char *s, const char *format, dmy_struct *dmy); double time_delta(dmy_struct *dmy_current, unsigned short int freq, int n); +void timer_continue(timer_struct *t); +void timer_init(timer_struct *t); +void timer_start(timer_struct *t); +void timer_stop(timer_struct *t); int update_step_vars(all_vars_struct *, veg_con_struct *, veg_hist_struct *); int invalid_date(unsigned short int calendar, dmy_struct *dmy); void validate_parameters(void); diff --git a/vic/drivers/shared_all/include/vic_version.h b/vic/drivers/shared_all/include/vic_version.h index d16be23af..70dae5726 100644 --- a/vic/drivers/shared_all/include/vic_version.h +++ b/vic/drivers/shared_all/include/vic_version.h @@ -31,11 +31,11 @@ #define STR(x) STR_HELPER(x) #ifndef VERSION -#define VERSION "5.0.0 Release Candidate 1: June 28, 2016" +#define VERSION "5.0.0 Release Candidate 2: August 18, 2016" #endif #ifndef SHORT_VERSION -#define SHORT_VERSION "5.0.0.rc1" +#define SHORT_VERSION "5.0.0.rc2" #endif #ifndef GIT_VERSION @@ -62,7 +62,7 @@ #elif defined(__ICC) || defined(__INTEL_COMPILER) /* Intel ICC/ICPC. ------------------------------------------ */ # define COMPILER "icc" -# define COMPILER_VERSION __version__ +# define COMPILER_VERSION __VERSION__ #elif defined(__GNUC__) || defined(__GNUG__) /* GNU GCC/G++. --------------------------------------------- */ diff --git a/vic/drivers/shared_all/src/cmd_proc.c b/vic/drivers/shared_all/src/cmd_proc.c index 9baa0d6b0..884eea9c5 100644 --- a/vic/drivers/shared_all/src/cmd_proc.c +++ b/vic/drivers/shared_all/src/cmd_proc.c @@ -136,10 +136,10 @@ print_license() fprintf(stdout, "\n Variable Infiltration Capacity (VIC) macroscale hydrologic\n"); fprintf(stdout, - " model version %s, Copyright (C) 2016 Computational Hydrology\n", + " model version %s, Copyright (C) 2016 Computational\n", SHORT_VERSION); fprintf(stdout, - " Group, Dept. of Civil and Environmental Engineering,\n"); + " Hydrology Group, Dept. of Civil and Environmental Engineering,\n"); fprintf(stdout, " University of Washington. VIC comes with ABSOLUTELY NO\n"); fprintf(stdout, diff --git a/vic/drivers/shared_all/src/compute_derived_state_vars.c b/vic/drivers/shared_all/src/compute_derived_state_vars.c index b2b557feb..c0a61cabb 100644 --- a/vic/drivers/shared_all/src/compute_derived_state_vars.c +++ b/vic/drivers/shared_all/src/compute_derived_state_vars.c @@ -209,8 +209,8 @@ compute_derived_state_vars(all_vars_struct *all_vars, energy[veg][band].T[1], soil_con->avg_temp); if (ErrorFlag == ERROR) { - log_err("Error in " - "estimate_layer_temperature_quick_flux"); + log_err("Error calculating layer temperature " + "using QUICK_FLUX option"); } } else { @@ -233,8 +233,7 @@ compute_derived_state_vars(all_vars_struct *all_vars, options.Nnode, options.Nlayer); if (ErrorFlag == ERROR) { - log_err("Error in " - "estimate_layer_temperature"); + log_err("Error calculating layer temperature"); } } diff --git a/vic/drivers/shared_all/src/compute_lake_params.c b/vic/drivers/shared_all/src/compute_lake_params.c index 7ac0f0e81..9ed1325bc 100644 --- a/vic/drivers/shared_all/src/compute_lake_params.c +++ b/vic/drivers/shared_all/src/compute_lake_params.c @@ -88,7 +88,7 @@ compute_lake_params(lake_con_struct *lake_con, // compute volume corresponding to mindepth ErrFlag = get_volume(*lake_con, lake_con->mindepth, &(lake_con->minvolume)); if (ErrFlag == ERROR) { - log_err("problem in get_volume(): depth %f volume %f", + log_err("Error calculating depth: depth %f volume %f", lake_con->mindepth, lake_con->minvolume); } } diff --git a/vic/drivers/shared_all/src/compute_treeline.c b/vic/drivers/shared_all/src/compute_treeline.c index 60b5c0ce2..0881ec940 100644 --- a/vic/drivers/shared_all/src/compute_treeline.c +++ b/vic/drivers/shared_all/src/compute_treeline.c @@ -41,7 +41,7 @@ * @brief Compute treeline. *****************************************************************************/ void -compute_treeline(atmos_data_struct *atmos, +compute_treeline(force_data_struct *force, dmy_struct *dmy, double avgJulyAirTemp, double *Tfactor, @@ -74,7 +74,7 @@ compute_treeline(atmos_data_struct *atmos, MonthCnt = 0; while (dmy[rec].month == 7) { for (i = 0; i < NF; i++) { - MonthSum += atmos[rec].air_temp[i]; + MonthSum += force[rec].air_temp[i]; MonthCnt++; } rec++; diff --git a/vic/drivers/shared_all/src/forcing_utils.c b/vic/drivers/shared_all/src/forcing_utils.c index 34ad4faf5..af9f7feef 100644 --- a/vic/drivers/shared_all/src/forcing_utils.c +++ b/vic/drivers/shared_all/src/forcing_utils.c @@ -37,7 +37,7 @@ average(double *ar, double sum = 0.; if (n <= 0) { - log_err("Error in calc_average: divide by zero or negative"); + log_err("Divide by zero or negative"); } else if (n == 1) { return ar[0]; @@ -94,7 +94,7 @@ air_density(double t, // rho = (p*1000)/(Rd * *t+CONST_TKFRZ) + (pv*1000)/(Rv * *t+CONST_TKFRZ); // approximation used in VIC - rho = 0.003486 * p / (275.0 + t); + rho = p / (CONST_RDAIR * (CONST_TKFRZ + t)); return rho; } diff --git a/vic/drivers/shared_all/src/generate_default_state.c b/vic/drivers/shared_all/src/generate_default_state.c index 9f0eaf5ec..7bb50f793 100644 --- a/vic/drivers/shared_all/src/generate_default_state.c +++ b/vic/drivers/shared_all/src/generate_default_state.c @@ -149,8 +149,8 @@ generate_default_state(all_vars_struct *all_vars, soil_con->frost_fract, soil_con->frost_slope, soil_con->FS_ACTIVE); if (ErrorFlag == ERROR) { - log_err("Error in " - "estimate_layer_ice_content_quick_flux"); + log_err("Error calculating layer temperature " + "using QUICK_FLUX option"); } } else { @@ -177,8 +177,7 @@ generate_default_state(all_vars_struct *all_vars, options.Nlayer, soil_con->FS_ACTIVE); if (ErrorFlag == ERROR) { - log_err("Error in " - "estimate_layer_ice_content"); + log_err("Error calculating layer ice content"); } } } diff --git a/vic/drivers/shared_all/src/get_parameters.c b/vic/drivers/shared_all/src/get_parameters.c index 288c4fa7c..45b8aa6f4 100644 --- a/vic/drivers/shared_all/src/get_parameters.c +++ b/vic/drivers/shared_all/src/get_parameters.c @@ -524,7 +524,7 @@ get_parameters(FILE *paramfile) } else { log_warn("Unrecognized option in the parameter file: %s " - "- check your spelling\n", optstr); + "- check your spelling", optstr); } } fgets(cmdstr, MAXSTRING, paramfile); diff --git a/vic/drivers/shared_all/src/history_metadata.c b/vic/drivers/shared_all/src/history_metadata.c index 46ece72d1..93a40aab2 100644 --- a/vic/drivers/shared_all/src/history_metadata.c +++ b/vic/drivers/shared_all/src/history_metadata.c @@ -1459,6 +1459,23 @@ set_output_met_data_info() strcpy(out_metadata[OUT_SWE_BAND].description, out_metadata[OUT_SWE].description); + /* Wall time spent inside vic_run [seconds] */ + strcpy(out_metadata[OUT_TIME_VICRUN_WALL].varname, "OUT_TIME_VICRUN_WALL"); + strcpy(out_metadata[OUT_TIME_VICRUN_WALL].long_name, "time_vicrun_wall"); + strcpy(out_metadata[OUT_TIME_VICRUN_WALL].standard_name, + "vic_run_wall_time"); + strcpy(out_metadata[OUT_TIME_VICRUN_WALL].units, "seconds"); + strcpy(out_metadata[OUT_TIME_VICRUN_WALL].description, + "Wall time spent inside vic_run"); + + /* CPU time spent inside vic_run [seconds] */ + strcpy(out_metadata[OUT_TIME_VICRUN_CPU].varname, "OUT_TIME_VICRUN_CPU"); + strcpy(out_metadata[OUT_TIME_VICRUN_CPU].long_name, "time_vicrun_cpu"); + strcpy(out_metadata[OUT_TIME_VICRUN_CPU].standard_name, "vic_run_cpu_time"); + strcpy(out_metadata[OUT_TIME_VICRUN_CPU].units, "seconds"); + strcpy(out_metadata[OUT_TIME_VICRUN_CPU].description, + "CPU time spent inside vic_run"); + if (options.FROZEN_SOIL) { out_metadata[OUT_FDEPTH].nelem = MAX_FRONTS; out_metadata[OUT_TDEPTH].nelem = MAX_FRONTS; @@ -1468,6 +1485,8 @@ set_output_met_data_info() out_metadata[OUT_SMFROZFRAC].nelem = options.Nlayer; out_metadata[OUT_SOIL_ICE].nelem = options.Nlayer; out_metadata[OUT_SOIL_LIQ].nelem = options.Nlayer; + out_metadata[OUT_SOIL_ICE_FRAC].nelem = options.Nlayer; + out_metadata[OUT_SOIL_LIQ_FRAC].nelem = options.Nlayer; out_metadata[OUT_SOIL_MOIST].nelem = options.Nlayer; out_metadata[OUT_SOIL_TEMP].nelem = options.Nlayer; out_metadata[OUT_SOIL_TNODE].nelem = options.Nnode; diff --git a/vic/drivers/shared_all/src/make_veg_var.c b/vic/drivers/shared_all/src/make_veg_var.c index edd2d7e6e..0c35e4257 100644 --- a/vic/drivers/shared_all/src/make_veg_var.c +++ b/vic/drivers/shared_all/src/make_veg_var.c @@ -49,16 +49,20 @@ make_veg_var(size_t veg_type_num) temp[i][j].NscaleFactor = calloc(options.Ncanopy, sizeof(*(temp[i][j]. NscaleFactor))); - check_alloc_status(temp[i][j].NscaleFactor, "Memory allocation error."); + check_alloc_status(temp[i][j].NscaleFactor, + "Memory allocation error."); temp[i][j].aPARLayer = calloc(options.Ncanopy, sizeof(*(temp[i][j].aPARLayer))); - check_alloc_status(temp[i][j].aPARLayer, "Memory allocation error."); + check_alloc_status(temp[i][j].aPARLayer, + "Memory allocation error."); temp[i][j].CiLayer = calloc(options.Ncanopy, sizeof(*(temp[i][j].CiLayer))); - check_alloc_status(temp[i][j].CiLayer, "Memory allocation error."); + check_alloc_status(temp[i][j].CiLayer, + "Memory allocation error."); temp[i][j].rsLayer = calloc(options.Ncanopy, sizeof(*(temp[i][j].rsLayer))); - check_alloc_status(temp[i][j].rsLayer, "Memory allocation error."); + check_alloc_status(temp[i][j].rsLayer, + "Memory allocation error."); } } } diff --git a/vic/drivers/shared_all/src/open_file.c b/vic/drivers/shared_all/src/open_file.c index 59004f0d1..0144bd83e 100644 --- a/vic/drivers/shared_all/src/open_file.c +++ b/vic/drivers/shared_all/src/open_file.c @@ -33,7 +33,7 @@ * @type Type has one of the associated values with it: * - "r" open for reading * - "w" truncate or create for writing - * - "a" append; open for writing at end of file, or create for + * - "a" append; open for writing at end of file, or create for * writing * - "r+" open for update (reading and writing) * - "w+" truncate or create for update diff --git a/vic/drivers/shared_all/src/print_library_shared.c b/vic/drivers/shared_all/src/print_library_shared.c index 0ce177fcb..a7dd131cf 100644 --- a/vic/drivers/shared_all/src/print_library_shared.c +++ b/vic/drivers/shared_all/src/print_library_shared.c @@ -231,53 +231,6 @@ print_energy_bal(energy_bal_struct *eb, fprintf(LOG_DEST, "\tsnow_flux : %f\n", eb->snow_flux); } -/****************************************************************************** - * @brief Print filenames structure. - *****************************************************************************/ -void -print_filenames(filenames_struct *fnames) -{ - fprintf(LOG_DEST, "filenames:\n"); - fprintf(LOG_DEST, "\tforcing[0] : %s\n", fnames->forcing[0]); - fprintf(LOG_DEST, "\tforcing[1] : %s\n", fnames->forcing[1]); - fprintf(LOG_DEST, "\tf_path_pfx[0]: %s\n", fnames->f_path_pfx[0]); - fprintf(LOG_DEST, "\tf_path_pfx[1]: %s\n", fnames->f_path_pfx[1]); - fprintf(LOG_DEST, "\tglobal : %s\n", fnames->global); - fprintf(LOG_DEST, "\tconstants : %s\n", fnames->constants); - fprintf(LOG_DEST, "\tdomain : %s\n", fnames->domain); - fprintf(LOG_DEST, "\tinit_state : %s\n", fnames->init_state); - fprintf(LOG_DEST, "\tlakeparam : %s\n", fnames->lakeparam); - fprintf(LOG_DEST, "\tresult_dir : %s\n", fnames->result_dir); - fprintf(LOG_DEST, "\tsnowband : %s\n", fnames->snowband); - fprintf(LOG_DEST, "\tsoil : %s\n", fnames->soil); - fprintf(LOG_DEST, "\tstatefile : %s\n", fnames->statefile); - fprintf(LOG_DEST, "\tveg : %s\n", fnames->veg); - fprintf(LOG_DEST, "\tveglib : %s\n", fnames->veglib); - fprintf(LOG_DEST, "\tlog_path : %s\n", fnames->log_path); -} - -/****************************************************************************** - * @brief Print file path structure. - *****************************************************************************/ -void -print_filep(filep_struct *fp) -{ - fprintf(LOG_DEST, "filep:\n"); - fprintf(LOG_DEST, "\tforcing[0] : %p\n", fp->forcing[0]); - fprintf(LOG_DEST, "\tforcing[1] : %p\n", fp->forcing[1]); - fprintf(LOG_DEST, "\tglobalparam: %p\n", fp->globalparam); - fprintf(LOG_DEST, "\tconstants : %p\n", fp->constants); - fprintf(LOG_DEST, "\tdomain : %p\n", fp->domain); - fprintf(LOG_DEST, "\tinit_state : %p\n", fp->init_state); - fprintf(LOG_DEST, "\tlakeparam : %p\n", fp->lakeparam); - fprintf(LOG_DEST, "\tsnowband : %p\n", fp->snowband); - fprintf(LOG_DEST, "\tsoilparam : %p\n", fp->soilparam); - fprintf(LOG_DEST, "\tstatefile : %p\n", fp->statefile); - fprintf(LOG_DEST, "\tveglib : %p\n", fp->veglib); - fprintf(LOG_DEST, "\tvegparam : %p\n", fp->vegparam); - fprintf(LOG_DEST, "\tlogfile : %p\n", fp->logfile); -} - /****************************************************************************** * @brief Print forcing type structure. *****************************************************************************/ @@ -329,6 +282,7 @@ print_global_param(global_param_struct *gp) fprintf(LOG_DEST, "\tstateday : %hu\n", gp->stateday); fprintf(LOG_DEST, "\tstatemonth : %hu\n", gp->statemonth); fprintf(LOG_DEST, "\tstateyear : %hu\n", gp->stateyear); + fprintf(LOG_DEST, "\tstatesec : %u\n", gp->statesec); } /****************************************************************************** diff --git a/vic/drivers/shared_all/src/put_data.c b/vic/drivers/shared_all/src/put_data.c index cea32433b..700310480 100644 --- a/vic/drivers/shared_all/src/put_data.c +++ b/vic/drivers/shared_all/src/put_data.c @@ -33,13 +33,14 @@ *****************************************************************************/ void put_data(all_vars_struct *all_vars, - atmos_data_struct *atmos, + force_data_struct *force, soil_con_struct *soil_con, veg_con_struct *veg_con, veg_lib_struct *veg_lib, lake_con_struct *lake_con, double **out_data, - save_data_struct *save_data) + save_data_struct *save_data, + timer_struct *timer) { extern global_param_struct global_param; extern option_struct options; @@ -129,31 +130,31 @@ put_data(all_vars_struct *all_vars, zero_output_list(out_data); // Set output versions of input forcings - out_data[OUT_AIR_TEMP][0] = atmos->air_temp[NR]; - out_data[OUT_DENSITY][0] = atmos->density[NR]; - out_data[OUT_LWDOWN][0] = atmos->longwave[NR]; - out_data[OUT_PREC][0] = atmos->out_prec; // mm over grid cell - out_data[OUT_PRESSURE][0] = atmos->pressure[NR] / PA_PER_KPA; - out_data[OUT_QAIR][0] = CONST_EPS * atmos->vp[NR] / - atmos->pressure[NR]; - out_data[OUT_RAINF][0] = atmos->out_rain; // mm over grid cell - out_data[OUT_REL_HUMID][0] = FRACT_TO_PERCENT * atmos->vp[NR] / - (atmos->vp[NR] + atmos->vpd[NR]); + out_data[OUT_AIR_TEMP][0] = force->air_temp[NR]; + out_data[OUT_DENSITY][0] = force->density[NR]; + out_data[OUT_LWDOWN][0] = force->longwave[NR]; + out_data[OUT_PREC][0] = force->out_prec; // mm over grid cell + out_data[OUT_PRESSURE][0] = force->pressure[NR] / PA_PER_KPA; + out_data[OUT_QAIR][0] = CONST_EPS * force->vp[NR] / + force->pressure[NR]; + out_data[OUT_RAINF][0] = force->out_rain; // mm over grid cell + out_data[OUT_REL_HUMID][0] = FRACT_TO_PERCENT * force->vp[NR] / + (force->vp[NR] + force->vpd[NR]); if (options.LAKES && lake_con->Cl[0] > 0) { - out_data[OUT_LAKE_CHAN_IN][0] = atmos->channel_in[NR]; // mm over grid cell + out_data[OUT_LAKE_CHAN_IN][0] = force->channel_in[NR]; // mm over grid cell } else { out_data[OUT_LAKE_CHAN_IN][0] = 0; } - out_data[OUT_SWDOWN][0] = atmos->shortwave[NR]; - out_data[OUT_SNOWF][0] = atmos->out_snow; // mm over grid cell - out_data[OUT_VP][0] = atmos->vp[NR] / PA_PER_KPA; - out_data[OUT_VPD][0] = atmos->vpd[NR] / PA_PER_KPA; - out_data[OUT_WIND][0] = atmos->wind[NR]; + out_data[OUT_SWDOWN][0] = force->shortwave[NR]; + out_data[OUT_SNOWF][0] = force->out_snow; // mm over grid cell + out_data[OUT_VP][0] = force->vp[NR] / PA_PER_KPA; + out_data[OUT_VPD][0] = force->vpd[NR] / PA_PER_KPA; + out_data[OUT_WIND][0] = force->wind[NR]; if (options.CARBON) { - out_data[OUT_CATM][0] = atmos->Catm[NR] / PPM_to_MIXRATIO; - out_data[OUT_FDIR][0] = atmos->fdir[NR]; - out_data[OUT_PAR][0] = atmos->par[NR]; + out_data[OUT_CATM][0] = force->Catm[NR] / PPM_to_MIXRATIO; + out_data[OUT_FDIR][0] = force->fdir[NR]; + out_data[OUT_PAR][0] = force->par[NR]; } else { out_data[OUT_CATM][0] = MISSING; @@ -228,7 +229,6 @@ put_data(all_vars_struct *all_vars, HasVeg, (1 - Clake), overstory, - depth, frost_fract, out_data); @@ -298,7 +298,6 @@ put_data(all_vars_struct *all_vars, 0, Clake, overstory, - depth, frost_fract, out_data); @@ -491,6 +490,12 @@ put_data(all_vars_struct *all_vars, out_data[OUT_SMLIQFRAC][index] = out_data[OUT_SOIL_LIQ][index] / out_data[OUT_SOIL_MOIST][index]; out_data[OUT_SMFROZFRAC][index] = 1 - out_data[OUT_SMLIQFRAC][index]; + out_data[OUT_SOIL_LIQ_FRAC][index] = out_data[OUT_SOIL_LIQ][index] / + (depth[index] * + MM_PER_M); + out_data[OUT_SOIL_ICE_FRAC][index] = out_data[OUT_SOIL_ICE][index] / + (depth[index] * + MM_PER_M); } out_data[OUT_DELSOILMOIST][0] -= save_data->total_soil_moist; out_data[OUT_DELSWE][0] = out_data[OUT_SWE][0] + @@ -563,6 +568,10 @@ put_data(all_vars_struct *all_vars, else { out_data[OUT_ENERGY_ERROR][0] = MISSING; } + + // vic_run run time + out_data[OUT_TIME_VICRUN_WALL][0] = timer->delta_wall; + out_data[OUT_TIME_VICRUN_CPU][0] = timer->delta_cpu; } /****************************************************************************** @@ -578,7 +587,6 @@ collect_wb_terms(cell_data_struct cell, bool HasVeg, double lakefactor, bool overstory, - double *depth, double *frost_fract, double **out_data) { @@ -692,11 +700,6 @@ collect_wb_terms(cell_data_struct cell, out_data[OUT_SOIL_LIQ][index] += tmp_moist * AreaFactor; out_data[OUT_SOIL_ICE][index] += tmp_ice * AreaFactor; - - out_data[OUT_SOIL_LIQ][index] = tmp_moist * AreaFactor / depth[index] * - MM_PER_M; - out_data[OUT_SOIL_ICE][index] = tmp_ice * AreaFactor / depth[index] * - MM_PER_M; } out_data[OUT_SOIL_WET][0] += cell.wetness * AreaFactor; out_data[OUT_ROOTMOIST][0] += cell.rootmoist * AreaFactor; @@ -1048,17 +1051,18 @@ collect_eb_terms(energy_bal_struct energy, *****************************************************************************/ void initialize_save_data(all_vars_struct *all_vars, - atmos_data_struct *atmos, + force_data_struct *atmos, soil_con_struct *soil_con, veg_con_struct *veg_con, veg_lib_struct *veg_lib, lake_con_struct *lake_con, double **out_data, - save_data_struct *save_data) + save_data_struct *save_data, + timer_struct *timer) { // Calling put data will populate the save data storage terms put_data(all_vars, atmos, soil_con, veg_con, veg_lib, lake_con, - out_data, save_data); + out_data, save_data, timer); zero_output_list(out_data); } diff --git a/vic/drivers/shared_all/src/timing.c b/vic/drivers/shared_all/src/timing.c new file mode 100644 index 000000000..2fb2c2c38 --- /dev/null +++ b/vic/drivers/shared_all/src/timing.c @@ -0,0 +1,97 @@ +/****************************************************************************** + * @section DESCRIPTION + * + * Routines to calculate and store model runtime timing. + * + * @section LICENSE + * + * The Variable Infiltration Capacity (VIC) macroscale hydrological model + * Copyright (C) 2014 The Land Surface Hydrology Group, Department of Civil + * and Environmental Engineering, University of Washington. + * + * The VIC model 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 2 + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + *****************************************************************************/ + +#include + +/****************************************************************************** + * @brief Get wall time + *****************************************************************************/ +double +get_wall_time() +{ + struct timeval time; + if (gettimeofday(&time, NULL)) { + log_err("Unable to get time of day") + } + return (double) time.tv_sec + (double) time.tv_usec * 0.000001; +} + +/****************************************************************************** + * @brief Get CPU time + *****************************************************************************/ +double +get_cpu_time() +{ + return (double) clock() / CLOCKS_PER_SEC; +} + +/****************************************************************************** + * @brief Initialize timer values + *****************************************************************************/ +void +timer_init(timer_struct *t) +{ + t->start_wall = 0; + t->start_cpu = 0; + + t->delta_wall = 0; + t->delta_cpu = 0; +} + +/****************************************************************************** + * @brief Start timer + *****************************************************************************/ +void +timer_start(timer_struct *t) +{ + timer_init(t); + + t->start_wall = get_wall_time(); + t->start_cpu = get_cpu_time(); +} + +/****************************************************************************** + * @brief Stop timer + *****************************************************************************/ +void +timer_stop(timer_struct *t) +{ + t->stop_wall = get_wall_time(); + t->stop_cpu = get_cpu_time(); + + t->delta_wall += t->stop_wall - t->start_wall; + t->delta_cpu += t->stop_cpu - t->start_cpu; +} + +/****************************************************************************** + * @brief Continue timer without resetting counters + *****************************************************************************/ +void +timer_continue(timer_struct *t) +{ + t->start_wall = get_wall_time(); + t->start_cpu = get_cpu_time(); +} diff --git a/vic/drivers/shared_all/src/vic_history.c b/vic/drivers/shared_all/src/vic_history.c index 51d85c98b..1696db343 100644 --- a/vic/drivers/shared_all/src/vic_history.c +++ b/vic/drivers/shared_all/src/vic_history.c @@ -52,7 +52,7 @@ alloc_out_data(size_t ngridcells, check_alloc_status((*out_data)[i][j], "Memory allocation error."); // initialize data member - for (k = 0; k < out_metadata[i].nelem; k++) { + for (k = 0; k < out_metadata[j].nelem; k++) { (*out_data)[i][j][k] = 0; } } @@ -180,13 +180,15 @@ alloc_aggdata(stream_struct *stream) nelem = out_metadata[stream->varid[j]].nelem; stream->aggdata[i][j] = calloc(nelem, sizeof(*(stream->aggdata[i][j]))); - check_alloc_status(stream->aggdata[i][j], "Memory allocation error."); + check_alloc_status(stream->aggdata[i][j], + "Memory allocation error."); for (k = 0; k < nelem; k++) { // TODO: Also allocate for nbins, for now just setting to size 1 stream->aggdata[i][j][k] = calloc(1, sizeof(*(stream->aggdata[i][j][k]))); - check_alloc_status(stream->aggdata[i][j][k], "Memory allocation error."); + check_alloc_status(stream->aggdata[i][j][k], + "Memory allocation error."); } } } diff --git a/vic/drivers/shared_all/src/vic_log.c b/vic/drivers/shared_all/src/vic_log.c index 1db8f635d..a11cdd2c7 100644 --- a/vic/drivers/shared_all/src/vic_log.c +++ b/vic/drivers/shared_all/src/vic_log.c @@ -112,25 +112,25 @@ initialize_log(void) * @brief Set global log destination *****************************************************************************/ void -setup_logging(int id) +setup_logging(int id, + char log_path[], + FILE **logfile) { - extern filenames_struct filenames; - extern filep_struct filep; - extern FILE *LOG_DEST; - char logfilename[MAXSTRING]; + extern FILE *LOG_DEST; + char logfilename[MAXSTRING]; - if (strcmp(filenames.log_path, "MISSING") != 0) { + if (strcmp(log_path, "MISSING") != 0) { // Create logfile name - get_logname(filenames.log_path, id, logfilename); + get_logname(log_path, id, logfilename); // Open Logfile - filep.logfile = open_file(logfilename, "w"); + *logfile = open_file(logfilename, "w"); // Print log file name to stderr log_info("Initialized Log File: %s", logfilename); // Set Log Destination - LOG_DEST = filep.logfile; + LOG_DEST = *logfile; // Write first line of log file log_info("Initialized Log File: %s", logfilename); diff --git a/vic/drivers/shared_image/include/vic_driver_shared_image.h b/vic/drivers/shared_image/include/vic_driver_shared_image.h index 2b906627f..5655aabda 100644 --- a/vic/drivers/shared_image/include/vic_driver_shared_image.h +++ b/vic/drivers/shared_image/include/vic_driver_shared_image.h @@ -28,7 +28,7 @@ #define VIC_DRIVER_SHARED_IMAGE_H #include -#include +#include #include #include @@ -154,16 +154,48 @@ typedef struct { double *Cv; /**< array of fractional coverage for nc_types */ } veg_con_map_struct; +/****************************************************************************** + * @brief file structures + *****************************************************************************/ +typedef struct { + FILE *forcing[MAX_FORCE_FILES]; /**< forcing data files */ + FILE *globalparam; /**< global parameters file */ + FILE *constants; /**< model constants parameter file */ + FILE *domain; /**< domain file */ + FILE *init_state; /**< initial model state file */ + FILE *paramfile; /**< parameter file */ + FILE *statefile; /**< output model state file */ + FILE *logfile; /**< log file */ +} filep_struct; + +/****************************************************************************** + * @brief This structure stores input and output filenames. + *****************************************************************************/ +typedef struct { + char forcing[MAX_FORCE_FILES][MAXSTRING]; /**< atmospheric forcing data file names */ + char f_path_pfx[MAX_FORCE_FILES][MAXSTRING]; /**< path and prefix for atmospheric forcing data file names */ + char global[MAXSTRING]; /**< global control file name */ + char domain[MAXSTRING]; /**< domain file name */ + char constants[MAXSTRING]; /**< model constants file name */ + char params[MAXSTRING]; /**< model parameters file name */ + char init_state[MAXSTRING]; /**< initial model state file name */ + char result_dir[MAXSTRING]; /**< directory where results will be written */ + char statefile[MAXSTRING]; /**< name of file in which to store model state */ + char log_path[MAXSTRING]; /**< Location to write log file to */ +} filenames_struct; + void add_nveg_to_global_domain(char *nc_name, domain_struct *global_domain); -void alloc_atmos(atmos_data_struct *atmos); +void alloc_force(force_data_struct *force); void alloc_veg_hist(veg_hist_struct *veg_hist); double air_density(double t, double p); double average(double *ar, size_t n); void check_init_state_file(void); -void free_atmos(atmos_data_struct *atmos); +void compare_ncdomain_with_global_domain(char *ncfile); +void free_force(force_data_struct *force); void free_veg_hist(veg_hist_struct *veg_hist); void get_domain_type(char *cmdstr); -size_t get_global_domain(char *fname, domain_struct *global_domain); +size_t get_global_domain(char *fname, domain_struct *global_domain, + bool coords_only); size_t get_nc_dimension(char *nc_name, char *dim_name); void get_nc_var_attr(char *nc_name, char *var_name, char *attr_name, char **attr); @@ -178,6 +210,8 @@ int get_nc_dtype(unsigned short int dtype); int get_nc_mode(unsigned short int format); void initialize_domain(domain_struct *domain); void initialize_domain_info(domain_info_struct *info); +void initialize_filenames(void); +void initialize_fileps(void); void initialize_global_structures(void); void initialize_history_file(nc_file_struct *nc, stream_struct *stream, dmy_struct *dmy_current); @@ -193,11 +227,11 @@ void initialize_soil_con(soil_con_struct *soil_con); void initialize_veg_con(veg_con_struct *veg_con); void parse_output_info(FILE *gp, stream_struct **output_streams, dmy_struct *dmy_current); -void print_atmos_data(atmos_data_struct *atmos); +void print_force_data(force_data_struct *force); void print_domain(domain_struct *domain, bool print_loc); void print_location(location_struct *location); void print_nc_file(nc_file_struct *nc); -void print_nc_var(nc_var_struct *nc_var, size_t ndims); +void print_nc_var(nc_var_struct *nc_var); void print_veg_con_map(veg_con_map_struct *veg_con_map); void put_nc_attr(int nc_id, int var_id, const char *name, const char *value); void set_force_type(char *cmdstr, int file_num, int *field); @@ -217,8 +251,9 @@ void vic_init(void); void vic_init_output(dmy_struct *dmy_current); void vic_restore(void); void vic_start(void); -void vic_store(dmy_struct *dmy_current); +void vic_store(dmy_struct *dmy_current, char *state_filename); void vic_write(stream_struct *stream, nc_file_struct *nc_hist_file, dmy_struct *dmy_current); void vic_write_output(dmy_struct *dmy); +void write_vic_timing_table(timer_struct *timers, char *driver); #endif diff --git a/vic/drivers/shared_image/include/vic_nc_log.h b/vic/drivers/shared_image/include/vic_image_log.h similarity index 96% rename from vic/drivers/shared_image/include/vic_nc_log.h rename to vic/drivers/shared_image/include/vic_image_log.h index 849b60700..151f2c191 100644 --- a/vic/drivers/shared_image/include/vic_nc_log.h +++ b/vic/drivers/shared_image/include/vic_image_log.h @@ -38,16 +38,12 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -#ifndef __vic_nc_log_h__ -#define __vic_nc_log_h__ +#ifndef __vic_image_log_h__ +#define __vic_image_log_h__ #include #include -void print_mpi_error_str(int error_code); - -void print_mpi_error_str(int error_code); - // Macros for logging #define clean_ncerrno(e) (e == NC_NOERR ? "None" : nc_strerror(e)) diff --git a/vic/drivers/shared_image/include/vic_mpi.h b/vic/drivers/shared_image/include/vic_mpi.h index 95c849059..c72d5131e 100644 --- a/vic/drivers/shared_image/include/vic_mpi.h +++ b/vic/drivers/shared_image/include/vic_mpi.h @@ -61,5 +61,6 @@ void mpi_map_decomp_domain(size_t ncells, size_t mpi_size, int **mpi_map_local_array_sizes, int **mpi_map_global_array_offsets, size_t **mpi_map_mapping_array); +void print_mpi_error_str(int error_code); #endif diff --git a/vic/drivers/shared_image/src/alloc_atmos.c b/vic/drivers/shared_image/src/alloc_atmos.c index dd34131cd..7438704e4 100644 --- a/vic/drivers/shared_image/src/alloc_atmos.c +++ b/vic/drivers/shared_image/src/alloc_atmos.c @@ -1,7 +1,7 @@ /****************************************************************************** * @section DESCRIPTION * - * Allocate and free memory for the atmos data struct + * Allocate and free memory for the force data struct * * @section LICENSE * @@ -27,93 +27,91 @@ #include /****************************************************************************** - * @brief Allocate memory for the atmos data structure. + * @brief Allocate memory for the force data structure. *****************************************************************************/ void -alloc_atmos(atmos_data_struct *atmos) +alloc_force(force_data_struct *force) { extern option_struct options; - atmos->air_temp = calloc(NR + 1, sizeof(*(atmos->air_temp))); - check_alloc_status(atmos->air_temp, "Memory allocation error."); + force->air_temp = calloc(NR + 1, sizeof(*(force->air_temp))); + check_alloc_status(force->air_temp, "Memory allocation error."); - atmos->density = calloc(NR + 1, sizeof(*(atmos->density))); - check_alloc_status(atmos->density, "Memory allocation error."); + force->density = calloc(NR + 1, sizeof(*(force->density))); + check_alloc_status(force->density, "Memory allocation error."); - atmos->longwave = calloc(NR + 1, sizeof(*(atmos->longwave))); - check_alloc_status(atmos->longwave, "Memory allocation error."); + force->longwave = calloc(NR + 1, sizeof(*(force->longwave))); + check_alloc_status(force->longwave, "Memory allocation error."); - atmos->prec = calloc(NR + 1, sizeof(*(atmos->prec))); - check_alloc_status(atmos->prec, "Memory allocation error."); + force->prec = calloc(NR + 1, sizeof(*(force->prec))); + check_alloc_status(force->prec, "Memory allocation error."); - atmos->pressure = calloc(NR + 1, sizeof(*(atmos->pressure))); - check_alloc_status(atmos->pressure, "Memory allocation error."); + force->pressure = calloc(NR + 1, sizeof(*(force->pressure))); + check_alloc_status(force->pressure, "Memory allocation error."); - atmos->shortwave = calloc(NR + 1, sizeof(*(atmos->shortwave))); - check_alloc_status(atmos->shortwave, "Memory allocation error."); + force->shortwave = calloc(NR + 1, sizeof(*(force->shortwave))); + check_alloc_status(force->shortwave, "Memory allocation error."); - atmos->snowflag = calloc(NR + 1, sizeof(*(atmos->snowflag))); - check_alloc_status(atmos->snowflag, "Memory allocation error."); + force->snowflag = calloc(NR + 1, sizeof(*(force->snowflag))); + check_alloc_status(force->snowflag, "Memory allocation error."); - atmos->vp = calloc(NR + 1, sizeof(*(atmos->vp))); - check_alloc_status(atmos->vp, "Memory allocation error."); + force->vp = calloc(NR + 1, sizeof(*(force->vp))); + check_alloc_status(force->vp, "Memory allocation error."); - atmos->vpd = calloc(NR + 1, sizeof(*(atmos->vpd))); - check_alloc_status(atmos->vpd, "Memory allocation error."); + force->vpd = calloc(NR + 1, sizeof(*(force->vpd))); + check_alloc_status(force->vpd, "Memory allocation error."); - atmos->wind = calloc(NR + 1, sizeof(*(atmos->wind))); - check_alloc_status(atmos->wind, "Memory allocation error."); + force->wind = calloc(NR + 1, sizeof(*(force->wind))); + check_alloc_status(force->wind, "Memory allocation error."); if (options.LAKES) { - atmos->channel_in = calloc(NR + 1, sizeof(*(atmos->channel_in))); - check_alloc_status(atmos->channel_in, "Memory allocation error."); - + force->channel_in = calloc(NR + 1, sizeof(*(force->channel_in))); + check_alloc_status(force->channel_in, "Memory allocation error."); } if (options.CARBON) { - atmos->Catm = calloc(NR + 1, sizeof(*(atmos->Catm))); - check_alloc_status(atmos->Catm, "Memory allocation error."); - - atmos->coszen = calloc(NR + 1, sizeof(*(atmos->coszen))); - check_alloc_status(atmos->coszen, "Memory allocation error."); + force->Catm = calloc(NR + 1, sizeof(*(force->Catm))); + check_alloc_status(force->Catm, "Memory allocation error."); - atmos->fdir = calloc(NR + 1, sizeof(*(atmos->fdir))); - check_alloc_status(atmos->fdir, "Memory allocation error."); + force->coszen = calloc(NR + 1, sizeof(*(force->coszen))); + check_alloc_status(force->coszen, "Memory allocation error."); - atmos->par = calloc(NR + 1, sizeof(*(atmos->par))); - check_alloc_status(atmos->par, "Memory allocation error."); + force->fdir = calloc(NR + 1, sizeof(*(force->fdir))); + check_alloc_status(force->fdir, "Memory allocation error."); + force->par = calloc(NR + 1, sizeof(*(force->par))); + check_alloc_status(force->par, "Memory allocation error."); } } /****************************************************************************** - * @brief Free memory for the atmos data structure. + * @brief Free memory for the force data structure. *****************************************************************************/ void -free_atmos(atmos_data_struct *atmos) +free_force(force_data_struct *force) { extern option_struct options; - if (atmos == NULL) { + if (force == NULL) { return; } - free(atmos->air_temp); - free(atmos->density); - free(atmos->longwave); - free(atmos->prec); - free(atmos->pressure); - free(atmos->shortwave); - free(atmos->snowflag); - free(atmos->vp); - free(atmos->vpd); - free(atmos->wind); + free(force->air_temp); + free(force->density); + free(force->longwave); + free(force->prec); + free(force->pressure); + free(force->shortwave); + free(force->snowflag); + free(force->vp); + free(force->vpd); + free(force->wind); if (options.LAKES) { - free(atmos->channel_in); + free(force->channel_in); } if (options.CARBON) { - free(atmos->Catm); - free(atmos->coszen); - free(atmos->fdir); - free(atmos->par); + free(force->Catm); + free(force->coszen); + free(force->fdir); + free(force->par); } } diff --git a/vic/drivers/shared_image/src/check_domain_info.c b/vic/drivers/shared_image/src/check_domain_info.c new file mode 100644 index 000000000..1095b05a9 --- /dev/null +++ b/vic/drivers/shared_image/src/check_domain_info.c @@ -0,0 +1,84 @@ +/****************************************************************************** + * @section DESCRIPTION + * + * Routines to compare the global domain to the other VIC input files, such as + * parameter or state files. + * + * @section LICENSE + * + * The Variable Infiltration Capacity (VIC) macroscale hydrological model + * Copyright (C) 2016 The Computational Hydrology Group, Department of Civil + * and Environmental Engineering, University of Washington. + * + * The VIC model 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 2 + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + *****************************************************************************/ + + #include + +/****************************************************************************** + * @brief Check that the cooridnates, dimensions, and mask variables in a + netcdf file matches the global domain. + *****************************************************************************/ +void +compare_ncdomain_with_global_domain(char *ncfile) +{ + extern domain_struct global_domain; + + domain_struct ncfile_domain; + + size_t i; + + ncfile_domain.info = global_domain.info; + + // read the domain info from ncfile (e.g. parameters file or state file) + get_global_domain(ncfile, &ncfile_domain, true); + + // using the ncfile_domain, we can compare the values to the global domain. + + // dimension shapes match (lat/lon) + if (global_domain.n_nx != ncfile_domain.n_nx) { + log_err("x dimension in parameters file does not match domain"); + } + if (global_domain.n_ny != ncfile_domain.n_ny) { + log_err("y dimension in parameters file does not match domain"); + } + + // loop over all grid cells and check that the two domains are identical + for (i = 0; i < global_domain.ncells_total; i++) { + // mask matches + if (ncfile_domain.locations[i].run < global_domain.locations[i].run) { + log_err("parameter file mask for gridcell %zu is zero and the " + "domain file specifies that this cell should be run", i); + } + // latitude matches + if (!assert_close_double(ncfile_domain.locations[i].latitude, + global_domain.locations[i].latitude, + 0, 0.01)) { + log_err("latitude in parameter (%lf) file does not match the " + "latitude in the domain file (%lf) for gridcell %zu", + ncfile_domain.locations[i].latitude, + global_domain.locations[i].latitude, i); + } + // longitude matches + if (!assert_close_double(ncfile_domain.locations[i].longitude, + global_domain.locations[i].longitude, + 0, 0.01)) { + log_err("longitude in parameter (%lf) file does not match the " + "longitude in the domain file (%lf) for gridcell %zu", + ncfile_domain.locations[i].longitude, + global_domain.locations[i].longitude, i); + } + } +} diff --git a/vic/drivers/shared_image/src/get_global_domain.c b/vic/drivers/shared_image/src/get_global_domain.c index 375145860..6dbdd0574 100644 --- a/vic/drivers/shared_image/src/get_global_domain.c +++ b/vic/drivers/shared_image/src/get_global_domain.c @@ -31,7 +31,8 @@ *****************************************************************************/ size_t get_global_domain(char *nc_name, - domain_struct *global_domain) + domain_struct *global_domain, + bool coords_only) { int *run = NULL; double *var = NULL; @@ -70,7 +71,6 @@ get_global_domain(char *nc_name, debug("%zu active grid cells found in domain mask", global_domain->ncells_active); - // if MASTER_PROC global_domain->locations = malloc(global_domain->ncells_total * sizeof(*global_domain->locations)); check_alloc_status(global_domain->locations, "Memory allocation error."); @@ -175,22 +175,25 @@ get_global_domain(char *nc_name, nc_name); } - // get area - // TBD: read var id from file - get_nc_field_double(nc_name, global_domain->info.area_var, - d2start, d2count, var); - for (i = 0; i < global_domain->ncells_total; i++) { - global_domain->locations[i].area = var[i]; - } + if (!coords_only) { + // get area + // TBD: read var id from file + get_nc_field_double(nc_name, global_domain->info.area_var, + d2start, d2count, var); + for (i = 0; i < global_domain->ncells_total; i++) { + global_domain->locations[i].area = var[i]; + } - // get fraction - // TBD: read var id from file - get_nc_field_double(nc_name, global_domain->info.frac_var, - d2start, d2count, var); - for (i = 0; i < global_domain->ncells_total; i++) { - global_domain->locations[i].frac = var[i]; + // get fraction + // TBD: read var id from file + get_nc_field_double(nc_name, global_domain->info.frac_var, + d2start, d2count, var); + for (i = 0; i < global_domain->ncells_total; i++) { + global_domain->locations[i].frac = var[i]; + } } + // free memory free(var); free(run); diff --git a/vic/drivers/shared_image/src/get_nc_dimension.c b/vic/drivers/shared_image/src/get_nc_dimension.c index 73690e434..cdbbb3943 100644 --- a/vic/drivers/shared_image/src/get_nc_dimension.c +++ b/vic/drivers/shared_image/src/get_nc_dimension.c @@ -44,12 +44,14 @@ get_nc_dimension(char *nc_name, // get dimension id status = nc_inq_dimid(nc_id, dim_name, &dim_id); - check_nc_status(status, "Error getting dimension id %s in %s", dim_name, nc_name); + check_nc_status(status, "Error getting dimension id %s in %s", dim_name, + nc_name); // get dimension size status = nc_inq_dimlen(nc_id, dim_id, &dim_size); - check_nc_status(status, "Error getting dimension size for dim %s in %s", dim_name, - nc_name); + check_nc_status(status, "Error getting dimension size for dim %s in %s", + dim_name, + nc_name); // close the netcdf file status = nc_close(nc_id); diff --git a/vic/drivers/shared_image/src/get_nc_var_attr.c b/vic/drivers/shared_image/src/get_nc_var_attr.c index 6906d3467..418f56277 100644 --- a/vic/drivers/shared_image/src/get_nc_var_attr.c +++ b/vic/drivers/shared_image/src/get_nc_var_attr.c @@ -46,12 +46,14 @@ get_nc_var_attr(char *nc_name, // get variable id status = nc_inq_varid(nc_id, var_name, &var_id); - check_nc_status(status, "Error getting variable id %s in %s", var_name, nc_name); + check_nc_status(status, "Error getting variable id %s in %s", var_name, + nc_name); // get size of the attribute status = nc_inq_attlen(nc_id, var_id, attr_name, &attr_len); - check_nc_status(status, "Error getting attribute length for %s:%s in %s", var_name, - attr_name, nc_name); + check_nc_status(status, "Error getting attribute length for %s:%s in %s", + var_name, + attr_name, nc_name); // allocate memory for attribute *attr = malloc((attr_len + 1) * sizeof(**attr)); @@ -59,8 +61,10 @@ get_nc_var_attr(char *nc_name, // read attribute text status = nc_get_att_text(nc_id, var_id, attr_name, *attr); - check_nc_status(status, "Error getting netCDF attribute %s for var %s in %s", attr_name, - var_name, nc_name); + check_nc_status(status, + "Error getting netCDF attribute %s for var %s in %s", + attr_name, + var_name, nc_name); // we need to null terminate the string ourselves according to NetCDF docs (*attr)[attr_len] = '\0'; diff --git a/vic/drivers/shared_image/src/get_nc_varndimensions.c b/vic/drivers/shared_image/src/get_nc_varndimensions.c index f6021dfec..1f4131eae 100644 --- a/vic/drivers/shared_image/src/get_nc_varndimensions.c +++ b/vic/drivers/shared_image/src/get_nc_varndimensions.c @@ -44,12 +44,15 @@ get_nc_varndimensions(char *nc_name, // get variable id status = nc_inq_varid(nc_id, var_name, &var_id); - check_nc_status(status, "Error getting variable id %s in %s", var_name, nc_name); + check_nc_status(status, "Error getting variable id %s in %s", var_name, + nc_name); // get number of dimensions status = nc_inq_varndims(nc_id, var_id, &ndims); - check_nc_status(status, "Error getting number of dimensions for var %s in %s", var_name, - nc_name); + check_nc_status(status, + "Error getting number of dimensions for var %s in %s", + var_name, + nc_name); // close the netcdf file status = nc_close(nc_id); diff --git a/vic/drivers/shared_image/src/initialize_files.c b/vic/drivers/shared_image/src/initialize_files.c new file mode 100644 index 000000000..22b97f651 --- /dev/null +++ b/vic/drivers/shared_image/src/initialize_files.c @@ -0,0 +1,71 @@ +/****************************************************************************** + * @section DESCRIPTION + * + * This subroutine initalizes all filefilenames before they are called by + * the model. + * + * @section LICENSE + * + * The Variable Infiltration Capacity (VIC) macroscale hydrological model + * Copyright (C) 2016 The Computational Hydrology Group, Department of Civil + * and Environmental Engineering, University of Washington. + * + * The VIC model 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 2 + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + *****************************************************************************/ + +#include + +/****************************************************************************** + * @brief Initialize all filenames before they are called by the + * model. + *****************************************************************************/ +void +initialize_filenames() +{ + extern filenames_struct filenames; + + size_t i; + + strcpy(filenames.init_state, "MISSING"); + strcpy(filenames.statefile, "MISSING"); + strcpy(filenames.constants, "MISSING"); + strcpy(filenames.params, "MISSING"); + strcpy(filenames.result_dir, "MISSING"); + strcpy(filenames.log_path, "MISSING"); + for (i = 0; i < 2; i++) { + strcpy(filenames.f_path_pfx[i], "MISSING"); + } +} + +/****************************************************************************** + * @brief Initialize all file pointers + *****************************************************************************/ +void +initialize_fileps() +{ + extern filep_struct filep; + + size_t i; + + filep.globalparam = NULL; + filep.constants = NULL; + filep.init_state = NULL; + filep.paramfile = NULL; + filep.statefile = NULL; + filep.logfile = NULL; + for (i = 0; i < 2; i++) { + filep.forcing[i] = NULL; + } +} diff --git a/vic/drivers/shared_image/src/print_library_shared_image.c b/vic/drivers/shared_image/src/print_library_shared_image.c index 79bd445b9..c3d33fb0b 100644 --- a/vic/drivers/shared_image/src/print_library_shared_image.c +++ b/vic/drivers/shared_image/src/print_library_shared_image.c @@ -30,31 +30,31 @@ * @brief Print atmos data structure. *****************************************************************************/ void -print_atmos_data(atmos_data_struct *atmos) +print_force_data(force_data_struct *force) { extern option_struct options; - fprintf(LOG_DEST, "atmos_data :\n"); - fprintf(LOG_DEST, "\tair_temp : %.4f\n", atmos->air_temp[0]); - fprintf(LOG_DEST, "\tdensity : %.4f\n", atmos->density[0]); - fprintf(LOG_DEST, "\tlongwave : %.4f\n", atmos->longwave[0]); - fprintf(LOG_DEST, "\tout_prec : %.4f\n", atmos->out_prec); - fprintf(LOG_DEST, "\tout_rain : %.4f\n", atmos->out_rain); - fprintf(LOG_DEST, "\tout_snow : %.4f\n", atmos->out_snow); - fprintf(LOG_DEST, "\tprec : %.4f\n", atmos->prec[0]); - fprintf(LOG_DEST, "\tpressure : %.4f\n", atmos->pressure[0]); - fprintf(LOG_DEST, "\tshortwave : %.4f\n", atmos->shortwave[0]); - fprintf(LOG_DEST, "\tsnowflag : %d\n", atmos->snowflag[0]); - fprintf(LOG_DEST, "\tvp : %.4f\n", atmos->vp[0]); - fprintf(LOG_DEST, "\tvpd : %.4f\n", atmos->vpd[0]); - fprintf(LOG_DEST, "\twind : %.4f\n", atmos->wind[0]); + fprintf(LOG_DEST, "force_data :\n"); + fprintf(LOG_DEST, "\tair_temp : %.4f\n", force->air_temp[0]); + fprintf(LOG_DEST, "\tdensity : %.4f\n", force->density[0]); + fprintf(LOG_DEST, "\tlongwave : %.4f\n", force->longwave[0]); + fprintf(LOG_DEST, "\tout_prec : %.4f\n", force->out_prec); + fprintf(LOG_DEST, "\tout_rain : %.4f\n", force->out_rain); + fprintf(LOG_DEST, "\tout_snow : %.4f\n", force->out_snow); + fprintf(LOG_DEST, "\tprec : %.4f\n", force->prec[0]); + fprintf(LOG_DEST, "\tpressure : %.4f\n", force->pressure[0]); + fprintf(LOG_DEST, "\tshortwave : %.4f\n", force->shortwave[0]); + fprintf(LOG_DEST, "\tsnowflag : %d\n", force->snowflag[0]); + fprintf(LOG_DEST, "\tvp : %.4f\n", force->vp[0]); + fprintf(LOG_DEST, "\tvpd : %.4f\n", force->vpd[0]); + fprintf(LOG_DEST, "\twind : %.4f\n", force->wind[0]); if (options.LAKES) { - fprintf(LOG_DEST, "\tchannel_in: %.4f\n", atmos->channel_in[0]); + fprintf(LOG_DEST, "\tchannel_in: %.4f\n", force->channel_in[0]); } if (options.CARBON) { - fprintf(LOG_DEST, "\tCatm : %.4f\n", atmos->Catm[0]); - fprintf(LOG_DEST, "\tfdir : %.4f\n", atmos->fdir[0]); - fprintf(LOG_DEST, "\tpar : %.4f\n", atmos->par[0]); + fprintf(LOG_DEST, "\tCatm : %.4f\n", force->Catm[0]); + fprintf(LOG_DEST, "\tfdir : %.4f\n", force->fdir[0]); + fprintf(LOG_DEST, "\tpar : %.4f\n", force->par[0]); } } @@ -157,21 +157,20 @@ print_nc_file(nc_file_struct *nc) * @brief Print netCDF variable structure. *****************************************************************************/ void -print_nc_var(nc_var_struct *nc_var, - size_t ndims) +print_nc_var(nc_var_struct *nc_var) { size_t i; fprintf(LOG_DEST, "nc_var:\n"); fprintf(LOG_DEST, "\tnc_varid: %d\n", nc_var->nc_varid); fprintf(LOG_DEST, "\tnc_type: %d\n", nc_var->nc_type); - fprintf(LOG_DEST, "\tnc_dimids:"); - for (i = 0; i < ndims; i++) { + fprintf(LOG_DEST, "\tnc_dimids (%zu):", nc_var->nc_dims); + for (i = 0; i < nc_var->nc_dims; i++) { fprintf(LOG_DEST, "\t%d", nc_var->nc_dimids[i]); } fprintf(LOG_DEST, "\n"); fprintf(LOG_DEST, "\tnc_counts:"); - for (i = 0; i < ndims; i++) { + for (i = 0; i < nc_var->nc_dims; i++) { fprintf(LOG_DEST, "\t%zu", nc_var->nc_counts[i]); } fprintf(LOG_DEST, "\n"); diff --git a/vic/drivers/shared_image/src/put_nc_attr.c b/vic/drivers/shared_image/src/put_nc_attr.c index ce1c3504d..90c05e842 100644 --- a/vic/drivers/shared_image/src/put_nc_attr.c +++ b/vic/drivers/shared_image/src/put_nc_attr.c @@ -38,5 +38,6 @@ put_nc_attr(int nc_id, int status; status = nc_put_att_text(nc_id, var_id, name, strlen(value), value); - check_nc_status(status, "Error adding %s attribute in ncid %d", name, nc_id); + check_nc_status(status, "Error adding %s attribute in ncid %d", name, + nc_id); } diff --git a/vic/drivers/shared_image/src/vic_alloc.c b/vic/drivers/shared_image/src/vic_alloc.c index c63a62da9..9c4a0b2cc 100644 --- a/vic/drivers/shared_image/src/vic_alloc.c +++ b/vic/drivers/shared_image/src/vic_alloc.c @@ -33,7 +33,7 @@ void vic_alloc(void) { extern all_vars_struct *all_vars; - extern atmos_data_struct *atmos; + extern force_data_struct *force; extern domain_struct local_domain; extern option_struct options; extern double ***out_data; @@ -47,9 +47,9 @@ vic_alloc(void) size_t i; size_t j; - // allocate memory for atmos structure - atmos = malloc(local_domain.ncells_active * sizeof(*atmos)); - check_alloc_status(atmos, "Memory allocation error."); + // allocate memory for force structure + force = malloc(local_domain.ncells_active * sizeof(*force)); + check_alloc_status(force, "Memory allocation error."); // allocate memory for veg_hist structure veg_hist = malloc(local_domain.ncells_active * sizeof(*veg_hist)); @@ -75,7 +75,6 @@ vic_alloc(void) // allocate memory for lake structure lake_con = malloc(local_domain.ncells_active * sizeof(*lake_con)); check_alloc_status(lake_con, "Memory allocation error."); - } // all_vars allocation @@ -92,8 +91,8 @@ vic_alloc(void) // allocate memory for individual grid cells for (i = 0; i < local_domain.ncells_active; i++) { - // atmos allocation - allocate enough memory for NR+1 steps - alloc_atmos(&(atmos[i])); + // force allocation - allocate enough memory for NR+1 steps + alloc_force(&(force[i])); // snow band allocation soil_con[i].AreaFract = calloc(options.SNOW_BAND, @@ -110,7 +109,8 @@ vic_alloc(void) check_alloc_status(soil_con[i].Pfactor, "Memory allocation error."); soil_con[i].AboveTreeLine = calloc(options.SNOW_BAND, sizeof(*(soil_con[i].AboveTreeLine))); - check_alloc_status(soil_con[i].AboveTreeLine, "Memory allocation error."); + check_alloc_status(soil_con[i].AboveTreeLine, + "Memory allocation error."); initialize_soil_con(&(soil_con[i])); @@ -136,16 +136,18 @@ vic_alloc(void) for (j = 0; j < veg_con_map[i].nv_active; j++) { veg_con[i][j].zone_depth = calloc(options.ROOT_ZONES, sizeof(*(veg_con[i][j].zone_depth))); - check_alloc_status(veg_con[i][j].zone_depth, "Memory allocation error."); + check_alloc_status(veg_con[i][j].zone_depth, + "Memory allocation error."); veg_con[i][j].zone_fract = calloc(options.ROOT_ZONES, sizeof(*(veg_con[i][j].zone_fract))); - check_alloc_status(veg_con[i][j].zone_fract, "Memory allocation error."); + check_alloc_status(veg_con[i][j].zone_fract, + "Memory allocation error."); if (options.CARBON) { veg_con[i][j].CanopLayerBnd = calloc(options.Ncanopy, sizeof(*(veg_con[i][j]. CanopLayerBnd))); - check_alloc_status(veg_con[i][j].CanopLayerBnd, "Memory allocation error."); - + check_alloc_status(veg_con[i][j].CanopLayerBnd, + "Memory allocation error."); } initialize_veg_con(&(veg_con[i][j])); } diff --git a/vic/drivers/shared_image/src/vic_finalize.c b/vic/drivers/shared_image/src/vic_finalize.c index b06cb272c..cc643876f 100644 --- a/vic/drivers/shared_image/src/vic_finalize.c +++ b/vic/drivers/shared_image/src/vic_finalize.c @@ -35,7 +35,7 @@ vic_finalize(void) extern size_t *filter_active_cells; extern size_t *mpi_map_mapping_array; extern all_vars_struct *all_vars; - extern atmos_data_struct *atmos; + extern force_data_struct *force; extern domain_struct global_domain; extern domain_struct local_domain; extern filep_struct filep; @@ -64,7 +64,7 @@ vic_finalize(void) int status; - if (mpi_rank == 0) { + if (mpi_rank == VIC_MPI_ROOT) { // close the global parameter file fclose(filep.globalparam); @@ -80,7 +80,7 @@ vic_finalize(void) } for (i = 0; i < local_domain.ncells_active; i++) { - free_atmos(&(atmos[i])); + free_force(&(force[i])); free(soil_con[i].AreaFract); free(soil_con[i].BandElev); free(soil_con[i].Tfactor); @@ -104,7 +104,7 @@ vic_finalize(void) free_streams(&output_streams); free_out_data(local_domain.ncells_active, out_data); - free(atmos); + free(force); free(soil_con); free(veg_con_map); free(veg_con); diff --git a/vic/drivers/shared_image/src/vic_image_run.c b/vic/drivers/shared_image/src/vic_image_run.c index 14f35347f..56bccf520 100644 --- a/vic/drivers/shared_image/src/vic_image_run.c +++ b/vic/drivers/shared_image/src/vic_image_run.c @@ -34,7 +34,7 @@ vic_image_run(dmy_struct *dmy_current) { extern size_t current; extern all_vars_struct *all_vars; - extern atmos_data_struct *atmos; + extern force_data_struct *force; extern domain_struct local_domain; extern option_struct options; extern global_param_struct global_param; @@ -49,6 +49,7 @@ vic_image_run(dmy_struct *dmy_current) char dmy_str[MAXSTRING]; size_t i; + timer_struct timer; // Print the current timestep info before running vic_run sprint_dmy(dmy_str, dmy_current); @@ -60,10 +61,15 @@ vic_image_run(dmy_struct *dmy_current) local_domain.locations[i].io_idx, dmy_str); update_step_vars(&(all_vars[i]), veg_con[i], veg_hist[i]); - vic_run(&(atmos[i]), &(all_vars[i]), dmy_current, &global_param, + + timer_start(&timer); + vic_run(&(force[i]), &(all_vars[i]), dmy_current, &global_param, &lake_con, &(soil_con[i]), veg_con[i], veg_lib[i]); - put_data(&(all_vars[i]), &(atmos[i]), &(soil_con[i]), veg_con[i], - veg_lib[i], &lake_con, out_data[i], &(save_data[i])); + timer_stop(&timer); + + put_data(&(all_vars[i]), &(force[i]), &(soil_con[i]), veg_con[i], + veg_lib[i], &lake_con, out_data[i], &(save_data[i]), + &timer); } for (i = 0; i < options.Noutstreams; i++) { agg_stream_data(&(output_streams[i]), dmy_current, out_data); diff --git a/vic/drivers/shared_image/src/vic_image_timing.c b/vic/drivers/shared_image/src/vic_image_timing.c new file mode 100644 index 000000000..64034ddd7 --- /dev/null +++ b/vic/drivers/shared_image/src/vic_image_timing.c @@ -0,0 +1,155 @@ +/****************************************************************************** + * @section DESCRIPTION + * + * Write vic timing table for image-like drivers + * + * @section LICENSE + * + * The Variable Infiltration Capacity (VIC) macroscale hydrological model + * Copyright (C) 2016 The Computational Hydrology Group, Department of Civil + * and Environmental Engineering, University of Washington. + * + * The VIC model 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 2 + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + *****************************************************************************/ + +#include + +/****************************************************************************** + * @brief VIC timing file + *****************************************************************************/ +void +write_vic_timing_table(timer_struct *timers, + char *driver) +{ + extern FILE *LOG_DEST; + extern filenames_struct filenames; + extern global_param_struct global_param; + extern int mpi_size; + + char machine[MAXSTRING]; + char user[MAXSTRING]; + time_t curr_date_time; + struct tm *timeinfo; + uid_t uid; + struct passwd *pw; + double ndays; + double nyears; + + // datestr + curr_date_time = time(NULL); + if (curr_date_time == -1) { + log_err("Failed to get the current time!"); + } + timeinfo = localtime(&curr_date_time); + + // hostname + if (gethostname(machine, MAXSTRING) != 0) { + strcpy(machine, "unknown"); + } + + // username + uid = geteuid(); + pw = getpwuid(uid); + + if (pw) { + strcpy(user, pw->pw_name); + } + else { + strcpy(user, "unknown"); + } + + // calculate run length + ndays = global_param.dt * global_param.nrecs / SEC_PER_DAY; + nyears = ndays / DAYS_PER_YEAR; + + fprintf(LOG_DEST, "\n"); + fprintf(LOG_DEST, + "------------------------------" + " VIC TIMING PROFILE " + "------------------------------\n\n"); + fprintf(LOG_DEST, " Date : %s", asctime(timeinfo)); + fprintf(LOG_DEST, " Compiler : %s (%s)\n", COMPILER, + COMPILER_VERSION); + fprintf(LOG_DEST, " Machine : %s\n", machine); + fprintf(LOG_DEST, " VIC User : %s\n", user); + fprintf(LOG_DEST, " VIC Version : %s\n", GIT_VERSION); + fprintf(LOG_DEST, " VIC GIT Version : %s\n", VERSION); + fprintf(LOG_DEST, " VIC_DRIVER : %s\n", driver); + fprintf(LOG_DEST, "\n"); + fprintf(LOG_DEST, " Global Param File : %s\n", filenames.global); + fprintf(LOG_DEST, " Domain File : %s\n", filenames.domain); + fprintf(LOG_DEST, " Start Date : %04hu-%02hu-%02hu-%05u\n", + global_param.startyear, global_param.startmonth, + global_param.startday, global_param.startsec); + fprintf(LOG_DEST, " Stop Date : %04hu-%02hu-%02hu\n", + global_param.endyear, global_param.endmonth, global_param.endday); + fprintf(LOG_DEST, " Nrecs : %zu\n", + global_param.nrecs); + fprintf(LOG_DEST, " Model Timestep (seconds) : %g\n", global_param.dt); + fprintf(LOG_DEST, " Snow Timestep (seconds) : %g\n", + global_param.snow_dt); + fprintf(LOG_DEST, " Runoff Timestep (seconds) : %g\n", + global_param.runoff_dt); + fprintf(LOG_DEST, " Atmos Timestep (seconds) : %g\n", + global_param.atmos_dt); + fprintf(LOG_DEST, "\n"); + + fprintf(LOG_DEST, " Total pes active : %d\n", mpi_size); + fprintf(LOG_DEST, " pes per node : %ld\n", + sysconf(_SC_NPROCESSORS_ONLN)); + + fprintf(LOG_DEST, "\n"); + + fprintf(LOG_DEST, " Overall Metrics\n"); + fprintf(LOG_DEST, " ---------------\n"); + fprintf(LOG_DEST, " Model Cost : %g pe-hrs/simulated_year\n", + mpi_size * timers[TIMER_VIC_ALL].delta_wall / SEC_PER_HOUR / + nyears); + fprintf(LOG_DEST, " Model Throughput : %g simulated_years/day\n", + nyears / (timers[TIMER_VIC_ALL].delta_wall / SEC_PER_DAY)); + fprintf(LOG_DEST, "\n"); + fprintf(LOG_DEST, " Timing Table:\n"); + fprintf(LOG_DEST, + "|------------|----------------------|----------------------|----------------------|----------------------|\n"); + fprintf(LOG_DEST, + "| Timer | Wall Time (secs) | CPU Time (secs) | Wall Time (secs/day) | CPU Time (secs/day) |\n"); + fprintf(LOG_DEST, + "|------------|----------------------|----------------------|----------------------|----------------------|\n"); + fprintf(LOG_DEST, "| Init Time | %20g | %20g | %20g | %20g |\n", + timers[TIMER_VIC_INIT].delta_wall, timers[TIMER_VIC_INIT].delta_cpu, + timers[TIMER_VIC_INIT].delta_wall / ndays, + timers[TIMER_VIC_INIT].delta_cpu / ndays); + fprintf(LOG_DEST, "| Run Time | %20g | %20g | %20g | %20g |\n", + timers[TIMER_VIC_RUN].delta_wall, timers[TIMER_VIC_RUN].delta_cpu, + timers[TIMER_VIC_RUN].delta_wall / ndays, + timers[TIMER_VIC_RUN].delta_cpu / ndays); + fprintf(LOG_DEST, "| Final Time | %20g | %20g | %20g | %20g |\n", + timers[TIMER_VIC_FINAL].delta_wall, + timers[TIMER_VIC_FINAL].delta_cpu, + timers[TIMER_VIC_FINAL].delta_wall / ndays, + timers[TIMER_VIC_FINAL].delta_cpu / ndays); + fprintf(LOG_DEST, "| Total Time | %20g | %20g | %20g | %20g |\n", + timers[TIMER_VIC_ALL].delta_wall, timers[TIMER_VIC_ALL].delta_cpu, + timers[TIMER_VIC_ALL].delta_wall / ndays, + timers[TIMER_VIC_ALL].delta_cpu / ndays); + fprintf(LOG_DEST, + "|------------|----------------------|----------------------|----------------------|----------------------|\n"); + fprintf(LOG_DEST, "\n"); + + fprintf(LOG_DEST, + "\n------------------------------" + " END VIC TIMING PROFILE " + "------------------------------\n\n"); +} diff --git a/vic/drivers/shared_image/src/vic_init.c b/vic/drivers/shared_image/src/vic_init.c index c1a9b1b14..d9e2a978b 100644 --- a/vic/drivers/shared_image/src/vic_init.c +++ b/vic/drivers/shared_image/src/vic_init.c @@ -74,14 +74,12 @@ vic_init(void) Cv_sum = malloc(local_domain.ncells_active * sizeof(*Cv_sum)); check_alloc_status(Cv_sum, "Memory allocation error."); - // allocate memory for variables to be read dvar = malloc(local_domain.ncells_active * sizeof(*dvar)); check_alloc_status(dvar, "Memory allocation error."); ivar = malloc(local_domain.ncells_active * sizeof(*ivar)); check_alloc_status(ivar, "Memory allocation error."); - // The method used to convert the NetCDF fields to VIC structures for // individual grid cells is to read a 2D slice and then loop over the // domain cells to assign the values to the VIC structures @@ -125,7 +123,7 @@ vic_init(void) // overstory for (j = 0; j < options.NVEGTYPES; j++) { d3start[0] = j; - get_scatter_nc_field_int(filenames.veglib, "overstory", + get_scatter_nc_field_int(filenames.params, "overstory", d3start, d3count, ivar); for (i = 0; i < local_domain.ncells_active; i++) { veg_lib[i][j].overstory = ivar[i]; @@ -135,7 +133,7 @@ vic_init(void) // rarc for (j = 0; j < options.NVEGTYPES; j++) { d3start[0] = j; - get_scatter_nc_field_double(filenames.veglib, "rarc", + get_scatter_nc_field_double(filenames.params, "rarc", d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { veg_lib[i][j].rarc = (double) dvar[i]; @@ -145,7 +143,7 @@ vic_init(void) // rmin for (j = 0; j < options.NVEGTYPES; j++) { d3start[0] = j; - get_scatter_nc_field_double(filenames.veglib, "rmin", + get_scatter_nc_field_double(filenames.params, "rmin", d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { veg_lib[i][j].rmin = (double) dvar[i]; @@ -155,7 +153,7 @@ vic_init(void) // wind height for (j = 0; j < options.NVEGTYPES; j++) { d3start[0] = j; - get_scatter_nc_field_double(filenames.veglib, "wind_h", + get_scatter_nc_field_double(filenames.params, "wind_h", d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { veg_lib[i][j].wind_h = (double) dvar[i]; @@ -165,7 +163,7 @@ vic_init(void) // RGL for (j = 0; j < options.NVEGTYPES; j++) { d3start[0] = j; - get_scatter_nc_field_double(filenames.veglib, "RGL", + get_scatter_nc_field_double(filenames.params, "RGL", d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { veg_lib[i][j].RGL = (double)dvar[i]; @@ -175,7 +173,7 @@ vic_init(void) // rad_atten for (j = 0; j < options.NVEGTYPES; j++) { d3start[0] = j; - get_scatter_nc_field_double(filenames.veglib, "rad_atten", + get_scatter_nc_field_double(filenames.params, "rad_atten", d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { veg_lib[i][j].rad_atten = (double) dvar[i]; @@ -185,7 +183,7 @@ vic_init(void) // wind_atten for (j = 0; j < options.NVEGTYPES; j++) { d3start[0] = j; - get_scatter_nc_field_double(filenames.veglib, "wind_atten", + get_scatter_nc_field_double(filenames.params, "wind_atten", d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { veg_lib[i][j].wind_atten = (double) dvar[i]; @@ -195,7 +193,7 @@ vic_init(void) // trunk_ratio for (j = 0; j < options.NVEGTYPES; j++) { d3start[0] = j; - get_scatter_nc_field_double(filenames.veglib, "trunk_ratio", + get_scatter_nc_field_double(filenames.params, "trunk_ratio", d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { veg_lib[i][j].trunk_ratio = (double) dvar[i]; @@ -208,7 +206,7 @@ vic_init(void) d4start[0] = j; for (k = 0; k < MONTHS_PER_YEAR; k++) { d4start[1] = k; - get_scatter_nc_field_double(filenames.veglib, "LAI", + get_scatter_nc_field_double(filenames.params, "LAI", d4start, d4count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { veg_lib[i][j].LAI[k] = (double) dvar[i]; @@ -225,7 +223,7 @@ vic_init(void) d4start[0] = j; for (k = 0; k < MONTHS_PER_YEAR; k++) { d4start[1] = k; - get_scatter_nc_field_double(filenames.veglib, "albedo", + get_scatter_nc_field_double(filenames.params, "albedo", d4start, d4count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { veg_lib[i][j].albedo[k] = (double) dvar[i]; @@ -239,7 +237,7 @@ vic_init(void) d4start[0] = j; for (k = 0; k < MONTHS_PER_YEAR; k++) { d4start[1] = k; - get_scatter_nc_field_double(filenames.veglib, "veg_rough", + get_scatter_nc_field_double(filenames.params, "veg_rough", d4start, d4count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { veg_lib[i][j].roughness[k] = (double) dvar[i]; @@ -252,7 +250,7 @@ vic_init(void) d4start[0] = j; for (k = 0; k < MONTHS_PER_YEAR; k++) { d4start[1] = k; - get_scatter_nc_field_double(filenames.veglib, "displacement", + get_scatter_nc_field_double(filenames.params, "displacement", d4start, d4count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { veg_lib[i][j].displacement[k] = (double) dvar[i]; @@ -280,7 +278,7 @@ vic_init(void) d4start[0] = j; for (k = 0; k < MONTHS_PER_YEAR; k++) { d4start[1] = k; - get_scatter_nc_field_double(filenames.veglib, "fcanopy", + get_scatter_nc_field_double(filenames.params, "fcanopy", d4start, d4count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { veg_lib[i][j].fcanopy[k] = (double) dvar[i]; @@ -294,7 +292,7 @@ vic_init(void) // Ctype for (j = 0; j < options.NVEGTYPES; j++) { d3start[0] = j; - get_scatter_nc_field_int(filenames.veglib, "Ctype", + get_scatter_nc_field_int(filenames.params, "Ctype", d3start, d3count, ivar); for (i = 0; i < local_domain.ncells_active; i++) { veg_lib[i][j].Ctype = ivar[i]; @@ -310,7 +308,7 @@ vic_init(void) // MaxCarboxRate for (j = 0; j < options.NVEGTYPES; j++) { d3start[0] = j; - get_scatter_nc_field_double(filenames.veglib, "MaxCarboxRate", + get_scatter_nc_field_double(filenames.params, "MaxCarboxRate", d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { veg_lib[i][j].MaxCarboxRate = (double) dvar[i]; @@ -324,7 +322,7 @@ vic_init(void) // MaxETransport or CO2Specificity for (j = 0; j < options.NVEGTYPES; j++) { d3start[0] = j; - get_scatter_nc_field_double(filenames.veglib, "MaxiE_or_CO2Spec", + get_scatter_nc_field_double(filenames.params, "MaxiE_or_CO2Spec", d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { if (dvar[i] < 0) { @@ -344,7 +342,7 @@ vic_init(void) // LightUseEff for (j = 0; j < options.NVEGTYPES; j++) { d3start[0] = j; - get_scatter_nc_field_double(filenames.veglib, "LUE", + get_scatter_nc_field_double(filenames.params, "LUE", d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { veg_lib[i][j].LightUseEff = (double) dvar[i]; @@ -359,7 +357,7 @@ vic_init(void) // Nscale flag for (j = 0; j < options.NVEGTYPES; j++) { d3start[0] = j; - get_scatter_nc_field_int(filenames.veglib, "Nscale", + get_scatter_nc_field_int(filenames.params, "Nscale", d3start, d3count, ivar); for (i = 0; i < local_domain.ncells_active; i++) { veg_lib[i][j].NscaleFlag = ivar[i]; @@ -374,7 +372,7 @@ vic_init(void) // Wnpp_inhib for (j = 0; j < options.NVEGTYPES; j++) { d3start[0] = j; - get_scatter_nc_field_double(filenames.veglib, "Wnpp_inhib", + get_scatter_nc_field_double(filenames.params, "Wnpp_inhib", d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { veg_lib[i][j].Wnpp_inhib = (double) dvar[i]; @@ -389,7 +387,7 @@ vic_init(void) // NPPfactor_sat for (j = 0; j < options.NVEGTYPES; j++) { d3start[0] = j; - get_scatter_nc_field_double(filenames.veglib, "NPPfactor_sat", + get_scatter_nc_field_double(filenames.params, "NPPfactor_sat", d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { veg_lib[i][j].NPPfactor_sat = (double) dvar[i]; @@ -405,9 +403,6 @@ vic_init(void) // read_soilparam() - // Nlayer - options.Nlayer = get_nc_dimension(filenames.soil, "nlayer"); - // Validate Nlayer if ((options.FULL_ENERGY || options.FROZEN_SOIL) && options.Nlayer < 3) { @@ -434,35 +429,35 @@ vic_init(void) } // b_infilt - get_scatter_nc_field_double(filenames.soil, "infilt", + get_scatter_nc_field_double(filenames.params, "infilt", d2start, d2count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { soil_con[i].b_infilt = (double) dvar[i]; } // Ds - get_scatter_nc_field_double(filenames.soil, "Ds", + get_scatter_nc_field_double(filenames.params, "Ds", d2start, d2count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { soil_con[i].Ds = (double) dvar[i]; } // Dsmax - get_scatter_nc_field_double(filenames.soil, "Dsmax", + get_scatter_nc_field_double(filenames.params, "Dsmax", d2start, d2count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { soil_con[i].Dsmax = (double) dvar[i]; } // Ws - get_scatter_nc_field_double(filenames.soil, "Ws", + get_scatter_nc_field_double(filenames.params, "Ws", d2start, d2count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { soil_con[i].Ws = (double) dvar[i]; } // c - get_scatter_nc_field_double(filenames.soil, "c", + get_scatter_nc_field_double(filenames.params, "c", d2start, d2count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { soil_con[i].c = (double) dvar[i]; @@ -471,7 +466,7 @@ vic_init(void) // expt: unsaturated hydraulic conductivity exponent for each layer for (j = 0; j < options.Nlayer; j++) { d3start[0] = j; - get_scatter_nc_field_double(filenames.soil, "expt", + get_scatter_nc_field_double(filenames.params, "expt", d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { soil_con[i].expt[j] = (double) dvar[i]; @@ -481,7 +476,7 @@ vic_init(void) // Ksat: saturated hydraulic conductivity for each layer for (j = 0; j < options.Nlayer; j++) { d3start[0] = j; - get_scatter_nc_field_double(filenames.soil, "Ksat", + get_scatter_nc_field_double(filenames.params, "Ksat", d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { soil_con[i].Ksat[j] = (double) dvar[i]; @@ -491,7 +486,7 @@ vic_init(void) // init_moist: initial soil moisture for cold start for (j = 0; j < options.Nlayer; j++) { d3start[0] = j; - get_scatter_nc_field_double(filenames.soil, "init_moist", + get_scatter_nc_field_double(filenames.params, "init_moist", d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { soil_con[i].init_moist[j] = (double) dvar[i]; @@ -501,7 +496,7 @@ vic_init(void) // phi_s for (j = 0; j < options.Nlayer; j++) { d3start[0] = j; - get_scatter_nc_field_double(filenames.soil, "phi_s", + get_scatter_nc_field_double(filenames.params, "phi_s", d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { soil_con[i].phi_s[j] = (double) dvar[i]; @@ -509,7 +504,7 @@ vic_init(void) } // elevation: mean grid cell elevation - get_scatter_nc_field_double(filenames.soil, "elev", + get_scatter_nc_field_double(filenames.params, "elev", d2start, d2count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { soil_con[i].elevation = (double) dvar[i]; @@ -518,7 +513,7 @@ vic_init(void) // depth: thickness for each soil layer for (j = 0; j < options.Nlayer; j++) { d3start[0] = j; - get_scatter_nc_field_double(filenames.soil, "depth", + get_scatter_nc_field_double(filenames.params, "depth", d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { soil_con[i].depth[j] = (double) dvar[i]; @@ -526,14 +521,14 @@ vic_init(void) } // avg_temp: mean grid temperature - get_scatter_nc_field_double(filenames.soil, "avg_T", + get_scatter_nc_field_double(filenames.params, "avg_T", d2start, d2count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { soil_con[i].avg_temp = (double) dvar[i]; } // dp: damping depth - get_scatter_nc_field_double(filenames.soil, "dp", + get_scatter_nc_field_double(filenames.params, "dp", d2start, d2count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { soil_con[i].dp = (double) dvar[i]; @@ -542,7 +537,7 @@ vic_init(void) // bubble: bubbling pressure for each soil layer for (j = 0; j < options.Nlayer; j++) { d3start[0] = j; - get_scatter_nc_field_double(filenames.soil, "bubble", + get_scatter_nc_field_double(filenames.params, "bubble", d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { soil_con[i].bubble[j] = (double) dvar[i]; @@ -552,7 +547,7 @@ vic_init(void) // quartz: quartz content for each soil layer for (j = 0; j < options.Nlayer; j++) { d3start[0] = j; - get_scatter_nc_field_double(filenames.soil, "quartz", + get_scatter_nc_field_double(filenames.params, "quartz", d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { soil_con[i].quartz[j] = (double) dvar[i]; @@ -562,7 +557,7 @@ vic_init(void) // bulk_dens_min: mineral bulk density for each soil layer for (j = 0; j < options.Nlayer; j++) { d3start[0] = j; - get_scatter_nc_field_double(filenames.soil, "bulk_density", + get_scatter_nc_field_double(filenames.params, "bulk_density", d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { soil_con[i].bulk_dens_min[j] = (double) dvar[i]; @@ -572,7 +567,7 @@ vic_init(void) // soil_dens_min: mineral soil density for each soil layer for (j = 0; j < options.Nlayer; j++) { d3start[0] = j; - get_scatter_nc_field_double(filenames.soil, "soil_density", + get_scatter_nc_field_double(filenames.params, "soil_density", d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { soil_con[i].soil_dens_min[j] = (double) dvar[i]; @@ -585,7 +580,7 @@ vic_init(void) // organic for (j = 0; j < options.Nlayer; j++) { d3start[0] = j; - get_scatter_nc_field_double(filenames.soil, "organic", + get_scatter_nc_field_double(filenames.params, "organic", d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { soil_con[i].organic[j] = (double) dvar[i]; @@ -595,7 +590,7 @@ vic_init(void) // bulk_dens_org: organic bulk density for each soil layer for (j = 0; j < options.Nlayer; j++) { d3start[0] = j; - get_scatter_nc_field_double(filenames.soil, "bulk_density_org", + get_scatter_nc_field_double(filenames.params, "bulk_density_org", d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { soil_con[i].bulk_dens_org[j] = (double) dvar[i]; @@ -605,7 +600,7 @@ vic_init(void) // soil_dens_org: organic soil density for each soil layer for (j = 0; j < options.Nlayer; j++) { d3start[0] = j; - get_scatter_nc_field_double(filenames.soil, "soil_density_org", + get_scatter_nc_field_double(filenames.params, "soil_density_org", d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { soil_con[i].soil_dens_org[j] = (double) dvar[i]; @@ -617,7 +612,7 @@ vic_init(void) // Note this value is multiplied with the maximum moisture in each layer for (j = 0; j < options.Nlayer; j++) { d3start[0] = j; - get_scatter_nc_field_double(filenames.soil, "Wcr_FRACT", + get_scatter_nc_field_double(filenames.params, "Wcr_FRACT", d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { soil_con[i].Wcr[j] = (double) dvar[i]; @@ -628,7 +623,7 @@ vic_init(void) // Note this value is multiplied with the maximum moisture in each layer for (j = 0; j < options.Nlayer; j++) { d3start[0] = j; - get_scatter_nc_field_double(filenames.soil, "Wpwp_FRACT", + get_scatter_nc_field_double(filenames.params, "Wpwp_FRACT", d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { soil_con[i].Wpwp[j] = (double) dvar[i]; @@ -636,21 +631,21 @@ vic_init(void) } // rough: soil roughness - get_scatter_nc_field_double(filenames.soil, "rough", + get_scatter_nc_field_double(filenames.params, "rough", d2start, d2count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { soil_con[i].rough = (double) dvar[i]; } // snow_rough: snow roughness - get_scatter_nc_field_double(filenames.soil, "snow_rough", + get_scatter_nc_field_double(filenames.params, "snow_rough", d2start, d2count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { soil_con[i].snow_rough = (double) dvar[i]; } // annual_prec: annual precipitation - get_scatter_nc_field_double(filenames.soil, "annual_prec", + get_scatter_nc_field_double(filenames.params, "annual_prec", d2start, d2count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { soil_con[i].annual_prec = (double) dvar[i]; @@ -659,7 +654,7 @@ vic_init(void) // resid_moist: residual moisture content for each layer for (j = 0; j < options.Nlayer; j++) { d3start[0] = j; - get_scatter_nc_field_double(filenames.soil, "resid_moist", + get_scatter_nc_field_double(filenames.params, "resid_moist", d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { soil_con[i].resid_moist[j] = (double) dvar[i]; @@ -667,7 +662,7 @@ vic_init(void) } // fs_active: frozen soil active flag - get_scatter_nc_field_int(filenames.soil, "fs_active", + get_scatter_nc_field_int(filenames.params, "fs_active", d2start, d2count, ivar); for (i = 0; i < local_domain.ncells_active; i++) { soil_con[i].FS_ACTIVE = (char) ivar[i]; @@ -676,7 +671,7 @@ vic_init(void) // spatial snow if (options.SPATIAL_SNOW) { // max_snow_distrib_slope - get_scatter_nc_field_double(filenames.soil, "max_snow_distrib_slope", + get_scatter_nc_field_double(filenames.params, "max_snow_distrib_slope", d2start, d2count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { soil_con[i].max_snow_distrib_slope = (double) dvar[i]; @@ -686,7 +681,7 @@ vic_init(void) // spatial frost if (options.SPATIAL_FROST) { // frost_slope: slope of frozen soil distribution - get_scatter_nc_field_double(filenames.soil, "frost_slope", + get_scatter_nc_field_double(filenames.params, "frost_slope", d2start, d2count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { soil_con[i].frost_slope = (double) dvar[i]; @@ -953,7 +948,7 @@ vic_init(void) // AreaFract: fraction of grid cell in each snow band for (j = 0; j < options.SNOW_BAND; j++) { d3start[0] = j; - get_scatter_nc_field_double(filenames.snowband, "AreaFract", + get_scatter_nc_field_double(filenames.params, "AreaFract", d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { soil_con[i].AreaFract[j] = (double) dvar[i]; @@ -962,7 +957,7 @@ vic_init(void) // elevation: elevation of each snow band for (j = 0; j < options.SNOW_BAND; j++) { d3start[0] = j; - get_scatter_nc_field_double(filenames.snowband, "elevation", + get_scatter_nc_field_double(filenames.params, "elevation", d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { soil_con[i].BandElev[j] = (double) dvar[i]; @@ -971,7 +966,7 @@ vic_init(void) // Pfactor: precipitation multiplier for each snow band for (j = 0; j < options.SNOW_BAND; j++) { d3start[0] = j; - get_scatter_nc_field_double(filenames.snowband, "Pfactor", + get_scatter_nc_field_double(filenames.params, "Pfactor", d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { soil_con[i].Pfactor[j] = (double) dvar[i]; @@ -1090,7 +1085,7 @@ vic_init(void) for (j = 0; j < options.NVEGTYPES; j++) { d3start[0] = j; - get_scatter_nc_field_double(filenames.veg, "Cv", + get_scatter_nc_field_double(filenames.params, "Cv", d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { veg_con_map[i].Cv[j] = (double) dvar[i]; @@ -1140,7 +1135,7 @@ vic_init(void) d4start[0] = j; for (k = 0; k < options.ROOT_ZONES; k++) { d4start[1] = k; - get_scatter_nc_field_double(filenames.veg, "root_depth", + get_scatter_nc_field_double(filenames.params, "root_depth", d4start, d4count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { vidx = veg_con_map[i].vidx[j]; @@ -1156,7 +1151,7 @@ vic_init(void) d4start[0] = j; for (k = 0; k < options.ROOT_ZONES; k++) { d4start[1] = k; - get_scatter_nc_field_double(filenames.veg, "root_fract", + get_scatter_nc_field_double(filenames.params, "root_fract", d4start, d4count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { vidx = veg_con_map[i].vidx[j]; @@ -1245,7 +1240,7 @@ vic_init(void) // sigma_slope for (j = 0; j < options.NVEGTYPES; j++) { d3start[0] = j; - get_scatter_nc_field_double(filenames.veg, "sigma_slope", + get_scatter_nc_field_double(filenames.params, "sigma_slope", d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { vidx = veg_con_map[i].vidx[j]; @@ -1262,7 +1257,7 @@ vic_init(void) // lag_one for (j = 0; j < options.NVEGTYPES; j++) { d3start[0] = j; - get_scatter_nc_field_double(filenames.veg, "lag_one", + get_scatter_nc_field_double(filenames.params, "lag_one", d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { vidx = veg_con_map[i].vidx[j]; @@ -1279,7 +1274,7 @@ vic_init(void) // fetch for (j = 0; j < options.NVEGTYPES; j++) { d3start[0] = j; - get_scatter_nc_field_double(filenames.veg, "fetch", + get_scatter_nc_field_double(filenames.params, "fetch", d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { vidx = veg_con_map[i].vidx[j]; @@ -1299,7 +1294,7 @@ vic_init(void) // read_lake parameters if (options.LAKES) { // lake_idx - get_scatter_nc_field_int(filenames.lakeparam, "lake_idx", + get_scatter_nc_field_int(filenames.params, "lake_idx", d2start, d2count, ivar); for (i = 0; i < local_domain.ncells_active; i++) { lake_con[i].lake_idx = ivar[i]; @@ -1307,7 +1302,7 @@ vic_init(void) lake_con[i].lake_idx < (int) veg_con[i][0].vegetat_type_num)) { log_err("cell %zu lake_idx is %d but we must have -1 " - "<= lake_idx < Nveg (%zu).\n", i, lake_con[i].lake_idx, + "<= lake_idx < Nveg (%zu).", i, lake_con[i].lake_idx, veg_con[i][0].vegetat_type_num); } if (lake_con[i].lake_idx != -1) { @@ -1316,7 +1311,7 @@ vic_init(void) } // numnod - get_scatter_nc_field_int(filenames.lakeparam, "numnod", + get_scatter_nc_field_int(filenames.params, "numnod", d2start, d2count, ivar); max_numnod = 0; for (i = 0; i < local_domain.ncells_active; i++) { @@ -1325,19 +1320,19 @@ vic_init(void) if (lake_con[i].numnod != 0) { log_err("cell %zu lake_idx is %d (lake not present) " "which requires numnod to be 0, but numnod is " - "%zu\n", i, lake_con[i].lake_idx, + "%zu.", i, lake_con[i].lake_idx, lake_con[i].numnod); } } else if (!(lake_con[i].numnod > 0 && lake_con[i].numnod < MAX_LAKE_NODES)) { log_err("cell %zu numnod is %zu but we must have 1 " - "<= numnod < %d.\n", i, lake_con[i].numnod, + "<= numnod < %d.", i, lake_con[i].numnod, MAX_LAKE_NODES); } else if (!(lake_con[i].numnod <= options.NLAKENODES)) { log_err("cell %zu numnod is %zu but this exceeds " - "the file lake_node dimension length of %zu.\n", + "the file lake_node dimension length of %zu.", i, lake_con[i].numnod, options.NLAKENODES); } if (lake_con[i].numnod > max_numnod) { @@ -1346,7 +1341,7 @@ vic_init(void) } // mindepth (minimum depth for which channel outflow occurs) - get_scatter_nc_field_double(filenames.lakeparam, "mindepth", + get_scatter_nc_field_double(filenames.params, "mindepth", d2start, d2count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { lake_con[i].mindepth = (double) dvar[i]; @@ -1354,19 +1349,19 @@ vic_init(void) if (lake_con[i].mindepth != 0) { log_err("cell %zu lake_idx is %d (lake not present) " "which requires mindepth to be 0, but mindepth " - "is %f\n", i, lake_con[i].lake_idx, + "is %f.", i, lake_con[i].lake_idx, lake_con[i].mindepth); } } else if (lake_con[i].lake_idx != -1 && !(lake_con[i].mindepth >= 0)) { - log_err("cell %zu mindepth is %f but must be >= 0.\n", + log_err("cell %zu mindepth is %f but must be >= 0.", i, lake_con[i].mindepth); } } // wfrac - get_scatter_nc_field_double(filenames.lakeparam, "wfrac", + get_scatter_nc_field_double(filenames.params, "wfrac", d2start, d2count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { lake_con[i].wfrac = (double) dvar[i]; @@ -1374,18 +1369,18 @@ vic_init(void) if (lake_con[i].wfrac != 0) { log_err("cell %zu lake_idx is %d (lake not present) " "which requires wfrac to be 0, but wfrac is " - "%f\n", i, lake_con[i].lake_idx, lake_con[i].wfrac); + "%f.", i, lake_con[i].lake_idx, lake_con[i].wfrac); } } else if (lake_con[i].lake_idx != -1 && !(lake_con[i].wfrac >= 0 && lake_con[i].wfrac <= 1)) { log_err("cell %zu wfrac is %f but we must have " - "0 <= wfrac <= 1.\n", i, lake_con[i].wfrac); + "0 <= wfrac <= 1.", i, lake_con[i].wfrac); } } // depth_in (initial depth for a cold start) - get_scatter_nc_field_double(filenames.lakeparam, "depth_in", + get_scatter_nc_field_double(filenames.params, "depth_in", d2start, d2count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { lake_con[i].depth_in = (double) dvar[i]; @@ -1393,19 +1388,19 @@ vic_init(void) if (lake_con[i].depth_in != 0) { log_err("cell %zu lake_idx is %d (lake not present) " "which requires depth_in to be 0, but depth_in is " - "%f\n", i, lake_con[i].lake_idx, + "%f.", i, lake_con[i].lake_idx, lake_con[i].depth_in); } } else if (lake_con[i].lake_idx != -1 && !(lake_con[i].depth_in >= 0)) { - log_err("cell %zu depth_in is %f but must be >= 0.\n", + log_err("cell %zu depth_in is %f but must be >= 0.", i, lake_con[i].depth_in); } } // rpercent - get_scatter_nc_field_double(filenames.lakeparam, "rpercent", + get_scatter_nc_field_double(filenames.params, "rpercent", d2start, d2count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { lake_con[i].rpercent = (double) dvar[i]; @@ -1413,7 +1408,7 @@ vic_init(void) if (lake_con[i].rpercent != 0) { log_err("cell %zu lake_idx is %d (lake not present) " "which requires rpercent to be 0, but rpercent is " - "%f\n", i, lake_con[i].lake_idx, + "%f.", i, lake_con[i].lake_idx, lake_con[i].rpercent); } } @@ -1421,7 +1416,7 @@ vic_init(void) !(lake_con[i].rpercent >= 0 && lake_con[i].rpercent <= 1)) { log_err("cell %zu rpercent is %f but we must have " - "0 <= rpercent <= 1.\n", i, lake_con[i].rpercent); + "0 <= rpercent <= 1.", i, lake_con[i].rpercent); } } @@ -1437,14 +1432,14 @@ vic_init(void) d3start[0] = j; // basin_depth - get_scatter_nc_field_double(filenames.lakeparam, "basin_depth", + get_scatter_nc_field_double(filenames.params, "basin_depth", d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { lake_con[i].z[j] = (double) dvar[i]; } // basin_area - get_scatter_nc_field_double(filenames.lakeparam, "basin_area", + get_scatter_nc_field_double(filenames.params, "basin_area", d3start, d3count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { lake_con[i].Cl[j] = (double) dvar[i]; @@ -1453,14 +1448,14 @@ vic_init(void) } else { // basin_depth - get_scatter_nc_field_double(filenames.lakeparam, "basin_depth", + get_scatter_nc_field_double(filenames.params, "basin_depth", d2start, d2count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { lake_con[i].z[0] = (double) dvar[i]; } // basin_area - get_scatter_nc_field_double(filenames.lakeparam, "basin_area", + get_scatter_nc_field_double(filenames.params, "basin_area", d2start, d2count, dvar); for (i = 0; i < local_domain.ncells_active; i++) { lake_con[i].Cl[0] = (double) dvar[i]; @@ -1474,36 +1469,36 @@ vic_init(void) if (lake_con[i].z[0] > 0) { log_err("cell %zu lake_idx is %d (lake not present) " "which requires max depth to be 0, but max depth " - "is %f\n", i, lake_con[i].lake_idx, + "is %f.", i, lake_con[i].lake_idx, lake_con[i].z[0]); } if (lake_con[i].Cl[0] > 0) { log_err("cell %zu lake_idx is %d (lake not present) " "which requires max area fraction to be 0, but " - "max area fraction is %f\n", i, + "max area fraction is %f.", i, lake_con[i].lake_idx, lake_con[i].Cl[0]); } } else { if (!(lake_con[i].z[0] > 0)) { log_err("cell %zu lake basin max depth is %f but must " - "be > 0.\n", i, lake_con[i].z[0]); + "be > 0.", i, lake_con[i].z[0]); } else if (!(lake_con[i].mindepth <= lake_con[i].z[0])) { log_err("cell %zu lake basin mindepth is %f but " - "must be <= max depth of %f\n", + "must be <= max depth of %f.", i, lake_con[i].mindepth, lake_con[i].z[0]); } if (!(lake_con[i].Cl[0] > 0 && lake_con[i].Cl[0] <= 1)) { log_err("cell %zu lake basin max area fraction is %f but " - "we must have 0 < max area fraction < 1\n", i, + "we must have 0 < max area fraction < 1.", i, lake_con[i].Cl[0]); } if (fabs(1 - lake_con[i].Cl[0] / veg_con[i][lake_con[i].lake_idx].Cv) > 0.01) { log_err("cell %zu lake basin max area fraction is %f but " "must == area fraction of veg tile containing " - "lake (%f)\n", i, lake_con[i].Cl[0], + "lake (%f).", i, lake_con[i].Cl[0], veg_con[i][lake_con[i].lake_idx].Cv); } else { @@ -1517,7 +1512,7 @@ vic_init(void) if (!(lake_con[i].z[j] > 0 && lake_con[i].z[j] < lake_con[i].z[j - 1])) { log_err("cell %zu lake basin node %zu depth is %f " - "but must be > 0 and < node %zu depth %f\n", + "but must be > 0 and < node %zu depth %f.", i, j, lake_con[i].z[j], j - 1, lake_con[i].z[j - 1]); } @@ -1525,7 +1520,7 @@ vic_init(void) lake_con[i].Cl[j] < lake_con[i].Cl[j - 1])) { log_err("cell %zu lake basin node %zu area fraction " "is %f but must be > 0 and < node %zu area " - "fraction %f\n", i, j, lake_con[i].Cl[j], j - 1, + "fraction %f.", i, j, lake_con[i].Cl[j], j - 1, lake_con[i].Cl[j - 1]); } } diff --git a/vic/drivers/shared_image/src/vic_init_output.c b/vic/drivers/shared_image/src/vic_init_output.c index 97bc7e7d5..ca0bbc0d4 100644 --- a/vic/drivers/shared_image/src/vic_init_output.c +++ b/vic/drivers/shared_image/src/vic_init_output.c @@ -24,8 +24,6 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *****************************************************************************/ -#include -#include #include /****************************************************************************** @@ -36,7 +34,7 @@ void vic_init_output(dmy_struct *dmy_current) { extern all_vars_struct *all_vars; - extern atmos_data_struct *atmos; + extern force_data_struct *force; extern domain_struct local_domain; extern filep_struct filep; extern MPI_Comm MPI_COMM_VIC; @@ -57,6 +55,7 @@ vic_init_output(dmy_struct *dmy_current) size_t streamnum; size_t nstream_vars[MAX_OUTPUT_STREAMS]; bool default_outputs = false; + timer_struct timer; // initialize the output data structures set_output_met_data_info(); @@ -66,9 +65,9 @@ vic_init_output(dmy_struct *dmy_current) // initialize the save data structures for (i = 0; i < local_domain.ncells_active; i++) { - initialize_save_data(&(all_vars[i]), &(atmos[i]), &(soil_con[i]), + initialize_save_data(&(all_vars[i]), &(force[i]), &(soil_con[i]), veg_con[i], veg_lib[i], &lake_con, out_data[i], - &(save_data[i])); + &(save_data[i]), &timer); } if (mpi_rank == VIC_MPI_ROOT) { @@ -186,20 +185,13 @@ vic_init_output(dmy_struct *dmy_current) output_streams[streamnum].nvars, output_streams[streamnum].varid, output_streams[streamnum].type); - - if (mpi_rank == VIC_MPI_ROOT) { - // open the netcdf history file - initialize_history_file(&(nc_hist_files[streamnum]), - &(output_streams[streamnum]), - dmy_current); - } } // validate streams validate_streams(&output_streams); } /****************************************************************************** - * @brief Initialize history files + * @brief Initialize history file *****************************************************************************/ void initialize_history_file(nc_file_struct *nc, @@ -646,6 +638,8 @@ initialize_nc_file(nc_file_struct *nc_file, size_t i; + nc_file->open = false; + // Set fill values nc_file->c_fillvalue = NC_FILL_CHAR; nc_file->s_fillvalue = NC_FILL_SHORT; diff --git a/vic/drivers/shared_image/src/vic_mpi_support.c b/vic/drivers/shared_image/src/vic_mpi_support.c index d88be8a2e..67436c7ec 100644 --- a/vic/drivers/shared_image/src/vic_mpi_support.c +++ b/vic/drivers/shared_image/src/vic_mpi_support.c @@ -28,8 +28,8 @@ /****************************************************************************** - * @brief Print MPI Error String to LOG_DEST, this function is used by loggers - ******************************************************************************/ +* @brief Print MPI Error String to LOG_DEST, this function is used by loggers +******************************************************************************/ void print_mpi_error_str(int error_code) { @@ -66,10 +66,7 @@ initialize_mpi(void) check_mpi_status(status, "MPI Error"); // set mpi error handling - MPI_Errhandler_set(MPI_COMM_VIC, MPI_ERRORS_RETURN); - - // set mpi error handling - MPI_Errhandler_set(MPI_COMM_VIC, MPI_ERRORS_RETURN); + MPI_Comm_set_errhandler(MPI_COMM_VIC, MPI_ERRORS_RETURN); status = MPI_Comm_size(MPI_COMM_VIC, &mpi_size); check_mpi_status(status, "MPI Error"); @@ -103,7 +100,7 @@ create_MPI_global_struct_type(MPI_Datatype *mpi_type) MPI_Datatype *mpi_types; // nitems has to equal the number of elements in global_param_struct - nitems = 31; + nitems = 32; blocklengths = malloc(nitems * sizeof(*blocklengths)); check_alloc_status(blocklengths, "Memory allocation error."); @@ -232,6 +229,10 @@ create_MPI_global_struct_type(MPI_Datatype *mpi_type) offsets[i] = offsetof(global_param_struct, statemonth); mpi_types[i++] = MPI_UNSIGNED_SHORT; + // unsigned int statesec; + offsets[i] = offsetof(global_param_struct, statesec); + mpi_types[i++] = MPI_UNSIGNED; + // unsigned short int stateyear; offsets[i] = offsetof(global_param_struct, stateyear); mpi_types[i++] = MPI_UNSIGNED_SHORT; @@ -255,8 +256,7 @@ create_MPI_global_struct_type(MPI_Datatype *mpi_type) // make sure that the we have the right number of elements if (i != (size_t) nitems) { - log_err("Miscount in create_MPI_global_struct_type(): " - "%zd not equal to %d\n", i, nitems); + log_err("Miscount: %zd not equal to %d.", i, nitems); } status = MPI_Type_create_struct(nitems, blocklengths, offsets, mpi_types, @@ -283,17 +283,17 @@ create_MPI_global_struct_type(MPI_Datatype *mpi_type) void create_MPI_filenames_struct_type(MPI_Datatype *mpi_type) { - extern MPI_Comm MPI_COMM_VIC; + extern MPI_Comm MPI_COMM_VIC; - int nitems; // number of elements in struct - int status; - int *blocklengths; - size_t i; - MPI_Aint *offsets; - MPI_Datatype *mpi_types; + int nitems; // number of elements in struct + int status; + int *blocklengths; + size_t i; + MPI_Aint *offsets; + MPI_Datatype *mpi_types; // nitems has to equal the number of elements in filenames_struct - nitems = 14; + nitems = 10; blocklengths = malloc(nitems * sizeof(*blocklengths)); check_alloc_status(blocklengths, "Memory allocation error."); @@ -338,34 +338,18 @@ create_MPI_filenames_struct_type(MPI_Datatype *mpi_type) offsets[i] = offsetof(filenames_struct, init_state); mpi_types[i++] = MPI_CHAR; - // char lakeparam[MAXSTRING]; - offsets[i] = offsetof(filenames_struct, lakeparam); + // char params[MAXSTRING]; + offsets[i] = offsetof(filenames_struct, params); mpi_types[i++] = MPI_CHAR; // char result_dir[MAXSTRING]; offsets[i] = offsetof(filenames_struct, result_dir); mpi_types[i++] = MPI_CHAR; - // char snowband[MAXSTRING]; - offsets[i] = offsetof(filenames_struct, snowband); - mpi_types[i++] = MPI_CHAR; - - // char soil[MAXSTRING]; - offsets[i] = offsetof(filenames_struct, soil); - mpi_types[i++] = MPI_CHAR; - // char statefile[MAXSTRING]; offsets[i] = offsetof(filenames_struct, statefile); mpi_types[i++] = MPI_CHAR; - // char veg[MAXSTRING]; - offsets[i] = offsetof(filenames_struct, veg); - mpi_types[i++] = MPI_CHAR; - - // char veglib[MAXSTRING]; - offsets[i] = offsetof(filenames_struct, veglib); - mpi_types[i++] = MPI_CHAR; - // char log_path[MAXSTRING]; offsets[i] = offsetof(filenames_struct, log_path); mpi_types[i++] = MPI_CHAR; @@ -373,8 +357,7 @@ create_MPI_filenames_struct_type(MPI_Datatype *mpi_type) // make sure that the we have the right number of elements if (i != (size_t) nitems) { - log_err("Miscount in create_MPI_filenames_struct_type(): " - "%zd not equal to %d\n", i, nitems); + log_err("Miscount: %zd not equal to %d.", i, nitems); } status = MPI_Type_create_struct(nitems, blocklengths, offsets, mpi_types, @@ -402,14 +385,14 @@ create_MPI_filenames_struct_type(MPI_Datatype *mpi_type) void create_MPI_location_struct_type(MPI_Datatype *mpi_type) { - extern MPI_Comm MPI_COMM_VIC; + extern MPI_Comm MPI_COMM_VIC; - int nitems; // number of elements in struct - int status; - int *blocklengths; - size_t i; - MPI_Aint *offsets; - MPI_Datatype *mpi_types; + int nitems; // number of elements in struct + int status; + int *blocklengths; + size_t i; + MPI_Aint *offsets; + MPI_Datatype *mpi_types; // nitems has to equal the number of elements in location_struct nitems = 9; @@ -468,8 +451,7 @@ create_MPI_location_struct_type(MPI_Datatype *mpi_type) // make sure that the we have the right number of elements if (i != (size_t) nitems) { - log_err("Miscount in create_MPI_location_struct_type(): " - "%zd not equal to %d\n", i, nitems); + log_err("Miscount: %zd not equal to %d.", i, nitems); } status = MPI_Type_create_struct(nitems, blocklengths, offsets, mpi_types, @@ -496,14 +478,14 @@ create_MPI_location_struct_type(MPI_Datatype *mpi_type) void create_MPI_option_struct_type(MPI_Datatype *mpi_type) { - extern MPI_Comm MPI_COMM_VIC; + extern MPI_Comm MPI_COMM_VIC; - int nitems; // number of elements in struct - int status; - int *blocklengths; - size_t i; - MPI_Aint *offsets; - MPI_Datatype *mpi_types; + int nitems; // number of elements in struct + int status; + int *blocklengths; + size_t i; + MPI_Aint *offsets; + MPI_Datatype *mpi_types; // nitems has to equal the number of elements in option_struct nitems = 53; @@ -738,8 +720,7 @@ create_MPI_option_struct_type(MPI_Datatype *mpi_type) // make sure that the we have the right number of elements if (i != (size_t) nitems) { - log_err("Miscount in create_MPI_option_struct_type(): " - "%zd not equal to %d\n", i, nitems); + log_err("Miscount: %zd not equal to %d.", i, nitems); } status = MPI_Type_create_struct(nitems, blocklengths, offsets, mpi_types, @@ -1406,8 +1387,7 @@ create_MPI_param_struct_type(MPI_Datatype *mpi_type) // make sure that the we have the right number of elements if (i != (size_t) nitems) { - log_err("Miscount in create_MPI_param_struct_type(): " - "%zd not equal to %d\n", i, nitems); + log_err("Miscount: %zd not equal to %d.", i, nitems); } status = MPI_Type_create_struct(nitems, blocklengths, offsets, mpi_types, @@ -1431,14 +1411,14 @@ create_MPI_param_struct_type(MPI_Datatype *mpi_type) void create_MPI_dmy_struct_type(MPI_Datatype *mpi_type) { - extern MPI_Comm MPI_COMM_VIC; + extern MPI_Comm MPI_COMM_VIC; - int nitems; // number of elements in struct - int status; - int *blocklengths; - size_t i; - MPI_Aint *offsets; - MPI_Datatype *mpi_types; + int nitems; // number of elements in struct + int status; + int *blocklengths; + size_t i; + MPI_Aint *offsets; + MPI_Datatype *mpi_types; // nitems has to equal the number of elements in dmy_struct nitems = 5; @@ -1481,7 +1461,7 @@ create_MPI_dmy_struct_type(MPI_Datatype *mpi_type) // make sure that the we have the right number of elements if (i != (size_t) nitems) { - log_err("Miscount: %zd not equal to %d\n", i, nitems); + log_err("Miscount: %zd not equal to %d.", i, nitems); } status = MPI_Type_create_struct(nitems, blocklengths, offsets, mpi_types, @@ -1506,15 +1486,15 @@ create_MPI_dmy_struct_type(MPI_Datatype *mpi_type) void create_MPI_alarm_struct_type(MPI_Datatype *mpi_type) { - extern MPI_Comm MPI_COMM_VIC; + extern MPI_Comm MPI_COMM_VIC; - int nitems; // number of elements in struct - int status; - int *blocklengths; - size_t i; - MPI_Aint *offsets; - MPI_Datatype *mpi_types; - MPI_Datatype mpi_dmy_type; + int nitems; // number of elements in struct + int status; + int *blocklengths; + size_t i; + MPI_Aint *offsets; + MPI_Datatype *mpi_types; + MPI_Datatype mpi_dmy_type; // nitems has to equal the number of elements in alarm_struct nitems = 6; @@ -1562,7 +1542,7 @@ create_MPI_alarm_struct_type(MPI_Datatype *mpi_type) // make sure that the we have the right number of elements if (i != (size_t) nitems) { - log_err("Miscount: %zd not equal to %d\n", i, nitems); + log_err("Miscount: %zd not equal to %d.", i, nitems); } status = MPI_Type_create_struct(nitems, blocklengths, offsets, mpi_types, @@ -1611,25 +1591,29 @@ map(size_t size, if (to_map == NULL && from_map == NULL) { for (i = 0; i < n; i++) { // type-agnostic version of to[i] = from[i]; - memcpy(to + i * size, from + i * size, size); + memcpy((void *)((char *)to + i * size), + (void *)((char *)from + i * size), size); } } if (to_map == NULL) { for (i = 0; i < n; i++) { // type-agnostic version of to[i] = from[from_map[i]]; - memcpy(to + i * size, from + from_map[i] * size, size); + memcpy((void *)((char *)to + i * size), + (void *)((char *)from + from_map[i] * size), size); } } else if (from_map == NULL) { for (i = 0; i < n; i++) { // type-agnostic version of to[to_map[i]] = from[i]; - memcpy(to + to_map[i] * size, from + i * size, size); + memcpy((void *)((char *)to + to_map[i] * size), + (void *)((char *)from + i * size), size); } } else { for (i = 0; i < n; i++) { // type-agnostic version of to[to_map[i]] = from[from_map[i]]; - memcpy(to + to_map[i] * size, from + from_map[i] * size, size); + memcpy((void *)((char *)to + to_map[i] * size), + (void *)((char *)from + from_map[i] * size), size); } } } @@ -1738,7 +1722,6 @@ gather_put_nc_field_double(int nc_id, dvar_remapped = malloc(global_domain.ncells_active * sizeof(*dvar_remapped)); check_alloc_status(dvar_remapped, "Memory allocation error."); - } // Gather the results from the nodes, result for the local node is in the // array *var (which is a function argument) @@ -1746,7 +1729,7 @@ gather_put_nc_field_double(int nc_id, dvar_gathered, mpi_map_local_array_sizes, mpi_map_global_array_offsets, MPI_DOUBLE, VIC_MPI_ROOT, MPI_COMM_VIC); -check_mpi_status(status, "MPI error."); + check_mpi_status(status, "MPI error."); if (mpi_rank == VIC_MPI_ROOT) { // remap the array map(sizeof(double), global_domain.ncells_active, NULL, @@ -1756,7 +1739,7 @@ check_mpi_status(status, "MPI error."); filter_active_cells, dvar_remapped, dvar); status = nc_put_vara_double(nc_id, var_id, start, count, dvar); - check_nc_status(status, "Error writing values"); + check_nc_status(status, "Error writing values."); // cleanup free(dvar); free(dvar_gathered); @@ -1806,7 +1789,6 @@ gather_put_nc_field_float(int nc_id, fvar_remapped = malloc(global_domain.ncells_active * sizeof(*fvar_remapped)); check_alloc_status(fvar_remapped, "Memory allocation error."); - } // Gather the results from the nodes, result for the local node is in the // array *var (which is a function argument) @@ -1874,13 +1856,12 @@ gather_put_nc_field_int(int nc_id, ivar_gathered = malloc(global_domain.ncells_active * sizeof(*ivar_gathered)); - check_alloc_status(ivar_gathered, "Memory allocation error."); + check_alloc_status(ivar_gathered, "Memory allocation error."); ivar_remapped = malloc(global_domain.ncells_active * sizeof(*ivar_remapped)); - check_alloc_status(ivar_remapped, "Memory allocation error."); - + check_alloc_status(ivar_remapped, "Memory allocation error."); } // Gather the results from the nodes, result for the local node is in the // array *var (which is a function argument) @@ -1888,7 +1869,7 @@ gather_put_nc_field_int(int nc_id, ivar_gathered, mpi_map_local_array_sizes, mpi_map_global_array_offsets, MPI_INT, VIC_MPI_ROOT, MPI_COMM_VIC); -check_mpi_status(status, "MPI error."); + check_mpi_status(status, "MPI error."); if (mpi_rank == VIC_MPI_ROOT) { // remap the array @@ -1948,13 +1929,12 @@ gather_put_nc_field_short(int nc_id, svar_gathered = malloc(global_domain.ncells_active * sizeof(*svar_gathered)); - check_alloc_status(svar_gathered, "Memory allocation error."); + check_alloc_status(svar_gathered, "Memory allocation error."); svar_remapped = malloc(global_domain.ncells_active * sizeof(*svar_remapped)); - check_alloc_status(svar_remapped, "Memory allocation error."); - + check_alloc_status(svar_remapped, "Memory allocation error."); } // Gather the results from the nodes, result for the local node is in the // array *var (which is a function argument) @@ -1962,7 +1942,7 @@ gather_put_nc_field_short(int nc_id, svar_gathered, mpi_map_local_array_sizes, mpi_map_global_array_offsets, MPI_SHORT, VIC_MPI_ROOT, MPI_COMM_VIC); -check_mpi_status(status, "MPI error."); + check_mpi_status(status, "MPI error."); if (mpi_rank == VIC_MPI_ROOT) { // remap the array @@ -2022,13 +2002,12 @@ gather_put_nc_field_schar(int nc_id, cvar_gathered = malloc(global_domain.ncells_active * sizeof(*cvar_gathered)); - check_alloc_status(cvar_gathered, "Memory allocation error."); + check_alloc_status(cvar_gathered, "Memory allocation error."); cvar_remapped = malloc(global_domain.ncells_active * sizeof(*cvar_remapped)); - check_alloc_status(cvar_remapped, "Memory allocation error."); - + check_alloc_status(cvar_remapped, "Memory allocation error."); } // Gather the results from the nodes, result for the local node is in the // array *var (which is a function argument) @@ -2036,7 +2015,7 @@ gather_put_nc_field_schar(int nc_id, cvar_gathered, mpi_map_local_array_sizes, mpi_map_global_array_offsets, MPI_CHAR, VIC_MPI_ROOT, MPI_COMM_VIC); -check_mpi_status(status, "MPI error."); + check_mpi_status(status, "MPI error."); if (mpi_rank == VIC_MPI_ROOT) { // remap the array @@ -2088,11 +2067,11 @@ get_scatter_nc_field_double(char *nc_name, dvar_filtered = malloc(global_domain.ncells_active * sizeof(*dvar_filtered)); - check_alloc_status(dvar, "Memory allocation error."); + check_alloc_status(dvar_filtered, "Memory allocation error."); dvar_mapped = malloc(global_domain.ncells_active * sizeof(*dvar_mapped)); - check_alloc_status(dvar_mapped, "Memory allocation error."); + check_alloc_status(dvar_mapped, "Memory allocation error."); get_nc_field_double(nc_name, var_name, start, count, dvar); // filter the active cells only @@ -2149,11 +2128,11 @@ get_scatter_nc_field_float(char *nc_name, fvar_filtered = malloc(global_domain.ncells_active * sizeof(*fvar_filtered)); - check_alloc_status(fvar_filtered, "Memory allocation error."); + check_alloc_status(fvar_filtered, "Memory allocation error."); fvar_mapped = malloc(global_domain.ncells_active * sizeof(*fvar_mapped)); - check_alloc_status(fvar_mapped, "Memory allocation error."); + check_alloc_status(fvar_mapped, "Memory allocation error."); get_nc_field_float(nc_name, var_name, start, count, fvar); // filter the active cells only @@ -2215,7 +2194,7 @@ get_scatter_nc_field_int(char *nc_name, ivar_mapped = malloc(global_domain.ncells_active * sizeof(*ivar_mapped)); - check_alloc_status(ivar_mapped, "Memory allocation error."); + check_alloc_status(ivar_mapped, "Memory allocation error."); get_nc_field_int(nc_name, var_name, start, count, ivar); // filter the active cells only @@ -2252,7 +2231,7 @@ get_scatter_nc_field_int(char *nc_name, size_t *filter_active_cells = NULL; size_t *mpi_map_mapping_array = NULL; // all_vars_struct *all_vars = NULL; -// atmos_data_struct *atmos = NULL; +// force_data_struct *force = NULL; // dmy_struct *dmy = NULL; filenames_struct filenames; filep_struct filep; diff --git a/vic/drivers/shared_image/src/vic_nc_info.c b/vic/drivers/shared_image/src/vic_nc_info.c index 492bf74cc..6867f5b84 100644 --- a/vic/drivers/shared_image/src/vic_nc_info.c +++ b/vic/drivers/shared_image/src/vic_nc_info.c @@ -58,6 +58,8 @@ set_nc_var_info(unsigned int varid, case OUT_SMFROZFRAC: case OUT_SOIL_ICE: case OUT_SOIL_LIQ: + case OUT_SOIL_ICE_FRAC: + case OUT_SOIL_LIQ_FRAC: case OUT_SOIL_MOIST: case OUT_SOIL_TEMP: nc_var->nc_dims = 4; @@ -133,6 +135,8 @@ set_nc_var_dimids(unsigned int varid, case OUT_SMFROZFRAC: case OUT_SOIL_ICE: case OUT_SOIL_LIQ: + case OUT_SOIL_ICE_FRAC: + case OUT_SOIL_LIQ_FRAC: case OUT_SOIL_MOIST: case OUT_SOIL_TEMP: nc_var->nc_dimids[0] = nc_hist_file->time_dimid; @@ -239,7 +243,7 @@ get_nc_dtype(unsigned short int dtype) type = NC_DOUBLE; break; default: - log_err("Unrecognized netCDF variable datatype %hu", dtype); + log_err("Unrecognized netCDF variable datatype: %hu", dtype); } return type; } diff --git a/vic/drivers/shared_image/src/vic_start.c b/vic/drivers/shared_image/src/vic_start.c index 5a356bacc..ca94d0bc4 100644 --- a/vic/drivers/shared_image/src/vic_start.c +++ b/vic/drivers/shared_image/src/vic_start.c @@ -63,7 +63,7 @@ vic_start(void) check_mpi_status(status, "MPI error."); // Set Log Destination - setup_logging(mpi_rank); + setup_logging(mpi_rank, filenames.log_path, &(filep.logfile)); if (mpi_rank == VIC_MPI_ROOT) { // set model constants @@ -74,11 +74,11 @@ vic_start(void) } // read domain info - get_global_domain(filenames.domain, &global_domain); + get_global_domain(filenames.domain, &global_domain, false); // add the number of vegetation type to the location info in the // global domain struct. This just makes life easier - add_nveg_to_global_domain(filenames.veg, &global_domain); + add_nveg_to_global_domain(filenames.params, &global_domain); // decompose the mask mpi_map_decomp_domain(global_domain.ncells_active, mpi_size, @@ -99,21 +99,20 @@ vic_start(void) } // get dimensions (number of vegetation types, soil zones, etc) - options.ROOT_ZONES = get_nc_dimension(filenames.soil, "root_zone"); - options.Nlayer = get_nc_dimension(filenames.soil, "nlayer"); - options.NVEGTYPES = get_nc_dimension(filenames.veg, "veg_class"); - if (options.SNOW_BAND > 1) { - if (options.SNOW_BAND != - get_nc_dimension(filenames.snowband, "snow_band")) { - log_err("Number of snow bands in global file does not " - "match parameter file"); - } + options.ROOT_ZONES = get_nc_dimension(filenames.params, "root_zone"); + options.Nlayer = get_nc_dimension(filenames.params, "nlayer"); + options.NVEGTYPES = get_nc_dimension(filenames.params, "veg_class"); + if (options.SNOW_BAND == SNOW_BAND_TRUE_BUT_UNSET) { + options.SNOW_BAND = get_nc_dimension(filenames.params, "snow_band"); } if (options.LAKES) { - options.NLAKENODES = get_nc_dimension(filenames.lakeparam, + options.NLAKENODES = get_nc_dimension(filenames.params, "lake_node"); } + // Validate the parameters file + compare_ncdomain_with_global_domain(filenames.params); + // Check that model parameters are valid validate_parameters(); } @@ -151,7 +150,7 @@ vic_start(void) local_domain.locations = malloc(local_domain.ncells_active * sizeof(*local_domain.locations)); if (local_domain.locations == NULL) { - log_err("malloc error in vic_start()\n"); + log_err("malloc error"); } for (i = 0; i < local_domain.ncells_active; i++) { initialize_location(&(local_domain.locations[i])); @@ -162,7 +161,7 @@ vic_start(void) mapped_locations = malloc(global_domain.ncells_active * sizeof(*mapped_locations)); if (mapped_locations == NULL) { - log_err("malloc error in vic_start()\n"); + log_err("malloc error"); } for (i = 0; i < global_domain.ncells_active; i++) { initialize_location(&(mapped_locations[i])); @@ -171,7 +170,7 @@ vic_start(void) active_locations = (location_struct *) malloc( global_domain.ncells_active * sizeof(location_struct)); if (active_locations == NULL) { - log_err("malloc error in vic_start()\n"); + log_err("malloc error"); } for (i = 0; i < global_domain.ncells_active; i++) { initialize_location(&(active_locations[i])); diff --git a/vic/drivers/shared_image/src/vic_store.c b/vic/drivers/shared_image/src/vic_store.c index a984ce737..f032e1c76 100644 --- a/vic/drivers/shared_image/src/vic_store.c +++ b/vic/drivers/shared_image/src/vic_store.c @@ -30,7 +30,8 @@ * @brief Save model state. *****************************************************************************/ void -vic_store(dmy_struct *dmy_current) +vic_store(dmy_struct *dmy_current, + char *filename) { extern filenames_struct filenames; extern all_vars_struct *all_vars; @@ -39,7 +40,6 @@ vic_store(dmy_struct *dmy_current) extern veg_con_map_struct *veg_con_map; extern int mpi_rank; - char filename[MAXSTRING]; int status; int v; size_t i; diff --git a/vic/drivers/shared_image/src/vic_write.c b/vic/drivers/shared_image/src/vic_write.c index 001a13ccc..af7620037 100644 --- a/vic/drivers/shared_image/src/vic_write.c +++ b/vic/drivers/shared_image/src/vic_write.c @@ -78,6 +78,13 @@ vic_write(stream_struct *stream, unsigned int varid; int status; + if (mpi_rank == VIC_MPI_ROOT) { + // If the output file is not open, initialize the history file now. + if (nc_hist_file->open == false) { + // open the netcdf history file + initialize_history_file(nc_hist_file, stream, dmy_current); + } + } // initialize dimids to invalid values - helps debugging for (i = 0; i < MAXDIMS; i++) { @@ -93,7 +100,6 @@ vic_write(stream_struct *stream, // allocate memory for variables to be stored dvar = malloc(local_domain.ncells_active * sizeof(*dvar)); check_alloc_status(dvar, "Memory allocation error"); - } } else if (nc_hist_file->nc_vars[k].nc_type == NC_FLOAT) { @@ -108,7 +114,6 @@ vic_write(stream_struct *stream, // allocate memory for variables to be stored ivar = malloc(local_domain.ncells_active * sizeof(*ivar)); check_alloc_status(ivar, "Memory allocation error"); - } } else if (nc_hist_file->nc_vars[k].nc_type == NC_SHORT) { @@ -116,7 +121,6 @@ vic_write(stream_struct *stream, // allocate memory for variables to be stored svar = malloc(local_domain.ncells_active * sizeof(*svar)); check_alloc_status(svar, "Memory allocation error"); - } } else if (nc_hist_file->nc_vars[k].nc_type == NC_CHAR) { @@ -124,7 +128,6 @@ vic_write(stream_struct *stream, // allocate memory for variables to be stored cvar = malloc(local_domain.ncells_active * sizeof(*cvar)); check_alloc_status(cvar, "Memory allocation error"); - } } else { @@ -220,7 +223,15 @@ vic_write(stream_struct *stream, // Advance the position in the history file stream->write_alarm.count++; - // TODO: Decide if it is time to close this file and open a new one. + if (raise_alarm(&(stream->write_alarm), dmy_current)) { + // close this history file + if (mpi_rank == VIC_MPI_ROOT) { + status = nc_close(nc_hist_file->nc_id); + check_nc_status(status, "Error closing history file"); + nc_hist_file->open = false; + } + reset_alarm(&(stream->write_alarm), dmy_current); + } // free memory if (dvar != NULL) { diff --git a/vic/vic_run/include/vic_def.h b/vic/vic_run/include/vic_def.h index b593fa33d..ddeffb3ce 100644 --- a/vic/vic_run/include/vic_def.h +++ b/vic/vic_run/include/vic_def.h @@ -27,6 +27,10 @@ #ifndef VIC_DEF_H #define VIC_DEF_H +#define _BSD_SOURCE +#define __USE_XOPEN +#define _GNU_SOURCE + #include #include #include @@ -38,6 +42,9 @@ #include #include #include +#include +#include +#include #include #include @@ -83,9 +90,9 @@ #define min(a, b) (a < b) ? a : b #define max(a, b) (a > b) ? a : b -extern size_t NR; /**< array index for atmos struct that indicates +extern size_t NR; /**< array index for force struct that indicates the model step avarage or sum */ -extern size_t NF; /**< array index loop counter limit for atmos +extern size_t NF; /**< array index loop counter limit for force struct that indicates the SNOW_STEP values */ char vic_run_ref_str[MAXSTRING]; @@ -706,7 +713,7 @@ typedef struct { } veg_hist_struct; /****************************************************************************** - * @brief This structure stores the atmospheric forcing data for each model + * @brief This structure stores the forcing data for each model * time step for a single grid cell. Each array stores the values for the * SNOW_STEPs during the current model step and the value for the entire model * step. The latter is referred to by array[NR]. Looping over the SNOW_STEPs @@ -734,7 +741,7 @@ typedef struct { double *vp; /**< atmospheric vapor pressure (kPa) */ double *vpd; /**< atmospheric vapor pressure deficit (kPa) */ double *wind; /**< wind speed (m/s) */ -} atmos_data_struct; +} force_data_struct; /****************************************************************************** * @brief This structure stores information about the time and date of the diff --git a/vic/vic_run/include/vic_log.h b/vic/vic_run/include/vic_log.h index b1c3ac911..819e17457 100644 --- a/vic/vic_run/include/vic_log.h +++ b/vic/vic_run/include/vic_log.h @@ -64,7 +64,7 @@ void finalize_logging(void); void get_logname(const char *path, int id, char *filename); void initialize_log(void); void print_trace(void); -void setup_logging(int id); +void setup_logging(int id, char log_path[], FILE **logfile); // Macros for logging @@ -130,7 +130,10 @@ void setup_logging(int id); #define check(A, M, ...) if (!(A)) {log_err(M, ## __VA_ARGS__); errno = 0; exit( \ EXIT_FAILURE); } -#define check_alloc_status(A, M, ...) if (A == NULL) {log_err(M, ## __VA_ARGS__); errno = 0; exit(EXIT_FAILURE); } +#define check_alloc_status(A, M, \ + ...) if (A == NULL) {log_err(M, ## __VA_ARGS__); \ + errno = 0; exit( \ + EXIT_FAILURE); } #define sentinel(M, ...) {log_err(M, ## __VA_ARGS__); errno = 0; exit( \ EXIT_FAILURE); } diff --git a/vic/vic_run/include/vic_physical_constants.h b/vic/vic_run/include/vic_physical_constants.h index 6aeaaf436..307b82be3 100644 --- a/vic/vic_run/include/vic_physical_constants.h +++ b/vic/vic_run/include/vic_physical_constants.h @@ -108,6 +108,8 @@ #define CONST_CPFW 4.188e3 /**< specific heat of fresh h2o ~ J/kg/K */ #define CONST_CPFWICE 4.2e3 /**< specific heat of fresh h2o ~ J/kg/K */ #define CONST_CPICE 2.11727e3 /**< specific heat of fresh ice ~ J/kg/K */ +/**< volumetric heats */ +#define CONST_VCPICE_WQ (CONST_CPICE * CONST_RHOFW) /**< heat capacity of fresh ice per volume of water equivalent ~ J/m^3/K */ /**< latent heats */ #define CONST_LATICE 3.337e5 /**< latent heat of fusion ~ J/kg */ #define CONST_LATVAP 2.501e6 /**< latent heat of evaporation ~ J/kg */ diff --git a/vic/vic_run/include/vic_run.h b/vic/vic_run/include/vic_run.h index 1a0eb8a5e..a812fa744 100644 --- a/vic/vic_run/include/vic_run.h +++ b/vic/vic_run/include/vic_run.h @@ -78,7 +78,7 @@ double calc_surf_energy_bal(double, double, double, double, double, double, double, double *, double *, double, double *, double *, int, int, size_t, size_t, double, size_t, unsigned short int, int, unsigned short int, - double *, double *, atmos_data_struct *, + double *, double *, force_data_struct *, dmy_struct *, energy_bal_struct *, layer_data_struct *, snow_data_struct *, soil_con_struct *, veg_var_struct *); @@ -243,7 +243,7 @@ int snow_intercept(double, double, double, double, double, double, double, double *, bool *, unsigned int *, double *, double *, double *, double *, double *, double *, double *, int, int, int, int, int, unsigned short int, double *, double *, - atmos_data_struct *, layer_data_struct *, soil_con_struct *, + force_data_struct *, layer_data_struct *, soil_con_struct *, veg_var_struct *); int snow_melt(double, double, double, double, double *, double, double *, double, double, double, double, double, double, double, double, @@ -271,7 +271,7 @@ double solve_snow(char, double, double, double, double, double, double, double, double *, double *, double *, double *, double *, double *, int, size_t, unsigned short int, unsigned short int, double, size_t, int, int *, double *, double *, dmy_struct *, - atmos_data_struct *, energy_bal_struct *, layer_data_struct *, + force_data_struct *, energy_bal_struct *, layer_data_struct *, snow_data_struct *, soil_con_struct *, veg_var_struct *); double solve_surf_energy_bal(double Tsurf, ...); int solve_T_profile(double *, double *, char *, unsigned int *, double *, @@ -293,7 +293,7 @@ int surface_fluxes(bool, double, double, double, double, double *, double *, double *, double *, double *, double *, double *, double *, double *, double *, double *, double *, double *, size_t, size_t, unsigned short int, double, unsigned short int, - unsigned short int, atmos_data_struct *, dmy_struct *, + unsigned short int, force_data_struct *, dmy_struct *, energy_bal_struct *, global_param_struct *, cell_data_struct *, snow_data_struct *, soil_con_struct *, veg_var_struct *, double, double, double, double *); @@ -315,7 +315,7 @@ double trapzd( int n); void tridia(int, double *, double *, double *, double *, double *); void tridiag(double *, double *, double *, double *, unsigned int); -int vic_run(atmos_data_struct *, all_vars_struct *, dmy_struct *, +int vic_run(force_data_struct *, all_vars_struct *, dmy_struct *, global_param_struct *, lake_con_struct *, soil_con_struct *, veg_con_struct *, veg_lib_struct *); double volumetric_heat_capacity(double, double, double, double); diff --git a/vic/vic_run/src/CalcBlowingSnow.c b/vic/vic_run/src/CalcBlowingSnow.c index 3baf925e6..84357919d 100644 --- a/vic/vic_run/src/CalcBlowingSnow.c +++ b/vic/vic_run/src/CalcBlowingSnow.c @@ -304,7 +304,7 @@ qromb(double (*funcd)(), } h[j + 1] = 0.25 * h[j]; } - log_err("Too many steps in routine qromb"); + log_err("Too many steps"); } /****************************************************************************** @@ -346,7 +346,7 @@ polint(double xa[], hp = xa[i + m] - x; w = c[i + 1] - d[i]; if ((den = ho - hp) == 0.0) { - log_err("Error in routine polint"); + log_err("interpolation error"); } den = w / den; d[i] = hp * den; @@ -427,7 +427,7 @@ rtnewt(double x1, get_shear(x2, &fh, &df, Ur, Zr); if ((fl > 0.0 && fh > 0.0) || (fl < 0.0 && fh < 0.0)) { - log_err("Root must be bracketed in rtnewt."); + log_err("Root must be bracketed"); } if (fl == 0.0) { @@ -479,7 +479,7 @@ rtnewt(double x1, xh = rts; } } - log_err("Maximum number of iterations exceeded in rtnewt."); + log_err("Maximum number of iterations exceeded"); } /****************************************************************************** @@ -678,7 +678,7 @@ shear_stress(double U10, get_shear(umax, &fh, &df, U10, 10.); if (fl < 0.0 && fh < 0.0) { - log_err("Solution in rtnewt surpasses upper boundary." + log_err("Solution surpasses upper boundary." "fl(%f)=%f, fh(%f)=%f", umin, fl, umax, fh); } diff --git a/vic/vic_run/src/IceEnergyBalance.c b/vic/vic_run/src/IceEnergyBalance.c index 01eb1d0e3..c1b7f63b2 100644 --- a/vic/vic_run/src/IceEnergyBalance.c +++ b/vic/vic_run/src/IceEnergyBalance.c @@ -178,7 +178,7 @@ IceEnergyBalance(double TSurf, /* Calculate advected heat flux from rain */ // Temporary fix for lake model. - *AdvectedEnergy = (CONST_CPFW * Tair * Rain) / Dt; + *AdvectedEnergy = (CONST_CPFW * CONST_RHOFW * Tair * Rain) / Dt; /* Calculate change in cold content */ /* No change in cold content in lake model */ diff --git a/vic/vic_run/src/SnowPackEnergyBalance.c b/vic/vic_run/src/SnowPackEnergyBalance.c index bd9cfd0f1..96a96b6e4 100644 --- a/vic/vic_run/src/SnowPackEnergyBalance.c +++ b/vic/vic_run/src/SnowPackEnergyBalance.c @@ -238,15 +238,15 @@ SnowPackEnergyBalance(double TSurf, Equation 7.3.12 from H.B.H. for rain falling on melting snowpack */ if (TMean == 0.) { - *AdvectedEnergy = (CONST_CPFW * (Tair) * Rain) / (Dt); + *AdvectedEnergy = (CONST_CPFW * CONST_RHOFW * (Tair) * Rain) / (Dt); } else { *AdvectedEnergy = 0.; } /* Calculate change in cold content */ - *DeltaColdContent = CONST_CPICE * SweSurfaceLayer * (TSurf - OldTSurf) / - (Dt); + *DeltaColdContent = CONST_VCPICE_WQ * SweSurfaceLayer * + (TSurf - OldTSurf) / (Dt); /* Calculate Ground Heat Flux */ if (SnowDepth > 0.) { diff --git a/vic/vic_run/src/calc_atmos_energy_bal.c b/vic/vic_run/src/calc_atmos_energy_bal.c index a7578ee13..c5fb94d4b 100644 --- a/vic/vic_run/src/calc_atmos_energy_bal.c +++ b/vic/vic_run/src/calc_atmos_energy_bal.c @@ -199,23 +199,20 @@ error_print_atmos_energy_bal(double Tcanopy, ErrorString = (char *)va_arg(ap, char *); // print variable values - fprintf(LOG_DEST, "%s", ErrorString); - fprintf(LOG_DEST, "ERROR: calc_atmos_energy_bal failed to converge to a " - "solution in root_brent. Variable values will be dumped " - "to the screen, check for invalid values.\n"); - fprintf(LOG_DEST, "Tcanopy = %f\n", Tcanopy); - fprintf(LOG_DEST, "LatentHeat = %f\n", LatentHeat); - fprintf(LOG_DEST, "NetRadiation = %f\n", NetRadiation); - fprintf(LOG_DEST, "Ra = %f\n", Ra); - fprintf(LOG_DEST, "Tair = %f\n", Tair); - fprintf(LOG_DEST, "atmos_density = %f\n", atmos_density); - fprintf(LOG_DEST, "InSensible = %f\n", InSensible); - - fprintf(LOG_DEST, "*SensibleHeat = %f\n", *SensibleHeat); - - fprintf(LOG_DEST, "Finished writing calc_atmos_energy_bal variables.\n" - "Try increasing CANOPY_DT to get model to complete cell.\n" - "Then check output for instabilities.\n"); + log_warn("Failure to converge to a solution in root_brent.\n" + "Check for invalid values.\n" + "Tcanopy = %f\n" + "LatentHeat = %f\n" + "NetRadiation = %f\n" + "Ra = %f\n" + "Tair = %f\n" + "atmos_density = %f\n" + "InSensible = %f\n" + "*SensibleHeat = %f\n" + "Try increasing CANOPY_DT to get model to complete cell.\n" + "Then check output for instabilities.", + Tcanopy, LatentHeat, NetRadiation, Ra, Tair, atmos_density, + InSensible, *SensibleHeat); return(ERROR); } @@ -284,19 +281,18 @@ error_print_atmos_moist_bal(double VPcanopy, ErrorString = (char *) va_arg(ap, char *); // print variable values - fprintf(LOG_DEST, "%s", ErrorString); - fprintf(LOG_DEST, "VPcanopy = %f\n", VPcanopy); - fprintf(LOG_DEST, "InLatent = %f\n", InLatent); - fprintf(LOG_DEST, "Lv = %f\n", Lv); - fprintf(LOG_DEST, "Ra = %f\n", Ra); - fprintf(LOG_DEST, "atmos_density = %f\n", atmos_density); - fprintf(LOG_DEST, "gamma = %f\n", gamma); - fprintf(LOG_DEST, "vp = %f\n", vp); - fprintf(LOG_DEST, "AtmosLatent = %f\n", *AtmosLatent); - - log_err("Finished writing calc_atmos_moist_bal variables.\nTry increasing " - "CANOPY_VP to get model to complete cell.\nThen check output for " - "instabilities."); + log_err("VPcanopy = %f\n" + "InLatent = %f\n" + "Lv = %f\n" + "Ra = %f\n" + "atmos_density = %f\n" + "gamma = %f\n" + "vp = %f\n" + "AtmosLatent = %f\n" + "Try increasing CANOPY_VP to get model to complete cell.\n" + "Then check output for instabilities.", + VPcanopy, InLatent, Lv, Ra, atmos_density, gamma, vp, + *AtmosLatent); return(0.0); } diff --git a/vic/vic_run/src/calc_surf_energy_bal.c b/vic/vic_run/src/calc_surf_energy_bal.c index 9070d7cde..642daee3d 100644 --- a/vic/vic_run/src/calc_surf_energy_bal.c +++ b/vic/vic_run/src/calc_surf_energy_bal.c @@ -80,7 +80,7 @@ calc_surf_energy_bal(double Le, unsigned short veg_class, double *CanopLayerBnd, double *dryFrac, - atmos_data_struct *atmos, + force_data_struct *force, dmy_struct *dmy, energy_bal_struct *energy, layer_data_struct *layer, @@ -202,11 +202,11 @@ calc_surf_energy_bal(double Le, kappa2 = energy->kappa[1]; // second layer conductivity Cs1 = energy->Cs[0]; // top layer heat capacity Cs2 = energy->Cs[1]; // second layer heat capacity - atmos_density = atmos->density[hidx]; // atmospheric density - atmos_pressure = atmos->pressure[hidx]; // atmospheric pressure - atmos_shortwave = atmos->shortwave[hidx]; // incoming shortwave radiation + atmos_density = force->density[hidx]; // atmospheric density + atmos_pressure = force->pressure[hidx]; // atmospheric pressure + atmos_shortwave = force->shortwave[hidx]; // incoming shortwave radiation if (options.CARBON) { - atmos_Catm = atmos->Catm[hidx]; // CO2 mixing ratio + atmos_Catm = force->Catm[hidx]; // CO2 mixing ratio } else { atmos_Catm = MISSING; @@ -690,7 +690,8 @@ calc_surf_energy_bal(double Le, if (snow->swq > 0) { // set snow energy terms snow->surf_temp = (Tsurf > 0) ? 0 : Tsurf; - snow->coldcontent = CONST_CPICE * snow->surf_temp * snow->swq; + snow->coldcontent = CONST_VCPICE_WQ * snow->surf_temp * + snow->swq; // recompute snow depth old_depth = snow->depth; diff --git a/vic/vic_run/src/compute_derived_lake_dimensions.c b/vic/vic_run/src/compute_derived_lake_dimensions.c index a97cc7280..23a8c5918 100644 --- a/vic/vic_run/src/compute_derived_lake_dimensions.c +++ b/vic/vic_run/src/compute_derived_lake_dimensions.c @@ -85,7 +85,7 @@ compute_derived_lake_dimensions(lake_var_struct *lake, } status = get_sarea(lake_con, depth, &(lake->surface[k])); if (status < 0) { - log_err("Error in get_sarea: record = %d, depth = %f, " + log_err("record = %d, depth = %f, " "sarea = %e", 0, depth, lake->surface[k]); } } @@ -93,11 +93,11 @@ compute_derived_lake_dimensions(lake_var_struct *lake, lake->sarea = lake->surface[0]; status = get_volume(lake_con, lake->ldepth, &tmp_volume); if (status < 0) { - log_err("Error in get_volume: record = %d, depth = %f, " + log_err("record = %d, depth = %f, " "volume = %e", 0, depth, tmp_volume); } else if (status > 0) { - log_err("Warning in get_volume: lake depth exceeds maximum; " + log_err("lake depth exceeds maximum; " "setting to maximum; record = %d", 0); } lake->volume = tmp_volume + lake->ice_water_eq; diff --git a/vic/vic_run/src/func_canopy_energy_bal.c b/vic/vic_run/src/func_canopy_energy_bal.c index e16afc55a..5f5a9d066 100644 --- a/vic/vic_run/src/func_canopy_energy_bal.c +++ b/vic/vic_run/src/func_canopy_energy_bal.c @@ -256,7 +256,8 @@ func_canopy_energy_bal(double Tfoliage, /* Calculate the advected energy */ - *AdvectedEnergy = (CONST_CPFW * Tcanopy * Rainfall) / (delta_t); + *AdvectedEnergy = (CONST_CPFW * CONST_RHOFW * Tcanopy * Rainfall) / + (delta_t); /* Calculate the amount of energy available for refreezing */ diff --git a/vic/vic_run/src/func_surf_energy_bal.c b/vic/vic_run/src/func_surf_energy_bal.c index 328472ab5..1fe7ef4ca 100644 --- a/vic/vic_run/src/func_surf_energy_bal.c +++ b/vic/vic_run/src/func_surf_energy_bal.c @@ -468,7 +468,7 @@ func_surf_energy_bal(double Ts, } if ((int) Error == ERROR) { - log_err("func_surf_energy_bal calling solve_T_profile"); + log_err("Error solving the temperature profile"); } /* Compute temperatures for calculations of ground heat flux, delta_H, and fusion */ if (!options.EXP_TRANS) { @@ -642,12 +642,12 @@ func_surf_energy_bal(double Ts, /* if thin snowpack, compute the change in energy stored in the pack */ if (INCLUDE_SNOW) { if (TMean > 0.) { - *deltaCC = CONST_CPICE * - (snow_swq - snow_water) * (0. - OldTSurf) / delta_t; + *deltaCC = CONST_VCPICE_WQ * (snow_swq - snow_water) * + (0. - OldTSurf) / delta_t; } else { - *deltaCC = CONST_CPICE * - (snow_swq - snow_water) * (TMean - OldTSurf) / delta_t; + *deltaCC = CONST_VCPICE_WQ * (snow_swq - snow_water) * + (TMean - OldTSurf) / delta_t; } *refreeze_energy = (snow_water * CONST_LATICE * snow_density) / delta_t; *deltaCC *= snow_coverage; // adjust for snow cover fraction diff --git a/vic/vic_run/src/ice_melt.c b/vic/vic_run/src/ice_melt.c index 13bb52e39..0892630c6 100644 --- a/vic/vic_run/src/ice_melt.c +++ b/vic/vic_run/src/ice_melt.c @@ -137,13 +137,13 @@ ice_melt(double z2, } /* Calculate cold contents */ - SurfaceCC = CONST_CPICE * SurfaceSwq * snow->surf_temp; - PackCC = CONST_CPICE * (PackSwq + PackIce) * snow->pack_temp; + SurfaceCC = CONST_VCPICE_WQ * SurfaceSwq * snow->surf_temp; + PackCC = CONST_VCPICE_WQ * (PackSwq + PackIce) * snow->pack_temp; if (air_temp > 0.0) { SnowFallCC = 0.0; } else { - SnowFallCC = CONST_CPICE * SnowFall * air_temp; + SnowFallCC = CONST_VCPICE_WQ * SnowFall * air_temp; } /* Distribute fresh snowfall */ @@ -171,13 +171,14 @@ ice_melt(double z2, DeltaPackCC = 0; } if (SurfaceSwq > 0.0) { - snow->surf_temp = SurfaceCC / (CONST_CPICE * SurfaceSwq); + snow->surf_temp = SurfaceCC / (CONST_VCPICE_WQ * SurfaceSwq); } else { snow->surf_temp = 0.0; } if (PackSwq + PackIce > 0.0) { - snow->pack_temp = PackCC / (CONST_CPICE * (PackSwq + PackIce)); + snow->pack_temp = PackCC / + (CONST_VCPICE_WQ * (PackSwq + PackIce)); } else { snow->pack_temp = 0.0; @@ -208,8 +209,7 @@ ice_melt(double z2, snow->surf_temp, 0, 1, 100., .067, .0123, &snow->transport); if ((int)snow->blowing_flux == ERROR) { - log_err("ice_melt.c has an error from the call to " - "CalcBlowingSnow. Exiting module."); + log_err("Error calculating blowing snow flux"); } snow->blowing_flux *= delta_t / CONST_RHOFW; @@ -508,8 +508,10 @@ ice_melt(double z2, if (PackSwq + PackIce > 0.0) { PackCC = (PackSwq + - PackIce) * CONST_CPICE * snow->pack_temp + PackRefreezeEnergy; - snow->pack_temp = PackCC / (CONST_CPICE * (PackSwq + PackIce)); + PackIce) * CONST_VCPICE_WQ * snow->pack_temp + + PackRefreezeEnergy; + snow->pack_temp = PackCC / (CONST_VCPICE_WQ * + (PackSwq + PackIce)); if (snow->pack_temp > 0.) { snow->pack_temp = 0.; } @@ -545,8 +547,8 @@ ice_melt(double z2, /* Update snow properties */ Ice = PackIce + PackSwq + SurfaceSwq; if (Ice > param.SNOW_MAX_SURFACE_SWE) { - SurfaceCC = CONST_CPICE * snow->surf_temp * SurfaceSwq; - PackCC = CONST_CPICE * snow->pack_temp * (PackSwq + PackIce); + SurfaceCC = CONST_VCPICE_WQ * snow->surf_temp * SurfaceSwq; + PackCC = CONST_VCPICE_WQ * snow->pack_temp * (PackSwq + PackIce); if (SurfaceSwq > param.SNOW_MAX_SURFACE_SWE) { PackCC += SurfaceCC * (SurfaceSwq - param.SNOW_MAX_SURFACE_SWE) / SurfaceSwq; @@ -565,8 +567,9 @@ ice_melt(double z2, PackSwq -= param.SNOW_MAX_SURFACE_SWE - SurfaceSwq; SurfaceSwq += param.SNOW_MAX_SURFACE_SWE - SurfaceSwq; } - snow->pack_temp = PackCC / (CONST_CPICE * (PackSwq + PackIce)); - snow->surf_temp = SurfaceCC / (CONST_CPICE * SurfaceSwq); + snow->pack_temp = PackCC / (CONST_VCPICE_WQ * + (PackSwq + PackIce)); + snow->surf_temp = SurfaceCC / (CONST_VCPICE_WQ * SurfaceSwq); } else { PackSwq = 0.0; diff --git a/vic/vic_run/src/lakes.eb.c b/vic/vic_run/src/lakes.eb.c index c858779cd..2242c7ccf 100644 --- a/vic/vic_run/src/lakes.eb.c +++ b/vic/vic_run/src/lakes.eb.c @@ -1896,13 +1896,13 @@ water_balance(lake_var_struct *lake, volume_save = lake->volume; ErrorFlag = get_depth(lake_con, lake->volume - lake->ice_water_eq, &ldepth); if (ErrorFlag == ERROR) { - log_err("Something went wrong in get_depth; " - "volume = %f, depth = %e", lake->volume, ldepth); + log_err("Error calculating depth: volume = %f, depth = %e", + lake->volume, ldepth); } ErrorFlag = get_sarea(lake_con, ldepth, &surfacearea); if (ErrorFlag == ERROR) { - log_err("Something went wrong in get_sarea; depth = %f, " - "sarea = %e", ldepth, surfacearea); + log_err("Error calculating area: depth = %f, sarea = %e", + ldepth, surfacearea); } // Estimate the new lake fraction (before recharge) @@ -2045,12 +2045,12 @@ water_balance(lake_var_struct *lake, // baseflow will only come from under the liquid portion of the lake ErrorFlag = get_depth(lake_con, lake->volume - lake->ice_water_eq, &ldepth); if (ErrorFlag == ERROR) { - log_err("Something went wrong in get_depth; volume = %f, " - "depth = %e", lake->volume, ldepth); + log_err("Error calculating depth: volume = %f, depth = %e", + lake->volume, ldepth); } ErrorFlag = get_sarea(lake_con, ldepth, &surfacearea); if (ErrorFlag == ERROR) { - log_err("Error in get_sarea; depth = %f, sarea = %e", + log_err("Error calculating area: depth = %f, sarea = %e", ldepth, surfacearea); } lake->baseflow_out = baseflow_out_mm * surfacearea / MM_PER_M; @@ -2065,8 +2065,8 @@ water_balance(lake_var_struct *lake, // Find new lake depth for runoff calculations ErrorFlag = get_depth(lake_con, lake->volume - lake->ice_water_eq, &ldepth); if (ErrorFlag == ERROR) { - log_err("Something went wrong in get_depth; volume = %f, " - "depth = %e", lake->volume, ldepth); + log_err("Error calculating depth: volume = %f, depth = %e", + lake->volume, ldepth); } // Compute runoff volume in m^3 and extract runoff volume from lake @@ -2126,8 +2126,8 @@ water_balance(lake_var_struct *lake, ErrorFlag = get_depth(lake_con, lake->volume - lake->ice_water_eq, &(lake->ldepth)); if (ErrorFlag == ERROR) { - log_err("Something went wrong in get_depth; volume = %f, " - "depth = %e", lake->volume, lake->ldepth); + log_err("Error calculating depth: volume = %f, depth = %e", + lake->volume, lake->ldepth); } /********************************************************************** diff --git a/vic/vic_run/src/root_brent.c b/vic/vic_run/src/root_brent.c index 1a9a3522e..d44284f2a 100644 --- a/vic/vic_run/src/root_brent.c +++ b/vic/vic_run/src/root_brent.c @@ -73,7 +73,6 @@ root_brent(double LowerBound, { extern parameters_struct param; - const char *Routine = "RootBrent"; va_list ap; /* Used in traversing variable argument list */ double a; double b; @@ -107,10 +106,9 @@ root_brent(double LowerBound, // If Function returns values of ERROR for both bounds, give up if (fa == ERROR && fb == ERROR) { - sprintf(ErrorString, "ERROR: %s: lower and upper bounds %f and %f " - "failed to bracket the root because the given function was " - "not defined at either point.\n", Routine, a, b); - log_warn("Root Brent is returning an error: %s", ErrorString); + log_warn("lower and upper bounds %f and %f " + "failed to bracket the root because the given function was " + "not defined at either point.", a, b); va_end(ap); return(ERROR); } @@ -146,11 +144,10 @@ root_brent(double LowerBound, if (fc == ERROR) { /* if we get here, we could not find a bound for which the function returns a valid value */ - sprintf(ErrorString, "ERROR: %s: the given function produced " + log_warn("the given function produced " "undefined values while attempting to " "bracket the root between %f and %f. Driver info: %s.", - Routine, LowerBound, UpperBound, vic_run_ref_str); - log_warn("Root Brent is returning an error: %s", ErrorString); + LowerBound, UpperBound, vic_run_ref_str); va_end(ap); return(ERROR); } @@ -189,13 +186,11 @@ root_brent(double LowerBound, fb = Function(b, ap); if (fb == ERROR) { /* Undefined function values in both directions - give up */ - sprintf(ErrorString, "ERROR: %s: the given function " - "produced undefined values while " - "attempting to bracket the root " - "between %f and %f. Driver info: %s.", - Routine, LowerBound, UpperBound, vic_run_ref_str); - log_warn("Root Brent is returning an error: %s", - ErrorString); + log_warn("the given function " + "produced undefined values while " + "attempting to bracket the root " + "between %f and %f. Driver info: %s.", + LowerBound, UpperBound, vic_run_ref_str); va_end(ap); return(ERROR); } @@ -207,13 +202,10 @@ root_brent(double LowerBound, fa = Function(a, ap); if (fa == ERROR) { /* Undefined function values in both directions - give up */ - sprintf(ErrorString, - "ERROR: %s: the given function produced undefined " - "values while attempting to bracket the root " - "between %f and %f. Driver info: %s.", - Routine, LowerBound, UpperBound, vic_run_ref_str); - log_warn("Root Brent is returning an error: %s", - ErrorString); + log_warn("the given function produced undefined " + "values while attempting to bracket the root " + "between %f and %f. Driver info: %s.", + LowerBound, UpperBound, vic_run_ref_str); va_end(ap); return(ERROR); } @@ -235,12 +227,10 @@ root_brent(double LowerBound, if (fc == ERROR) { /* if we get here, we could not find a bound for which the function returns a valid value */ - sprintf(ErrorString, - "ERROR: %s: the given function produced undefined " - "values while attempting to bracket the root between " - "%f and %f. Driver info: %s.", - Routine, LowerBound, UpperBound, vic_run_ref_str); - log_warn("Root Brent is returning an error: %s", ErrorString); + log_warn("the given function produced undefined " + "values while attempting to bracket the root between " + "%f and %f. Driver info: %s.", + LowerBound, UpperBound, vic_run_ref_str); va_end(ap); return(ERROR); } @@ -260,11 +250,9 @@ root_brent(double LowerBound, } if ((fa * fb) >= 0) { /* if we get here, the lower and upper bounds did not bracket the root */ - sprintf(ErrorString, - "WARNING: %s: lower and upper bounds %f and %f failed to " - "bracket the root. Driver info: %s.", - Routine, a, b, vic_run_ref_str); - log_warn("Root Brent is returning an error: %s", ErrorString); + log_warn("lower and upper bounds %f and %f failed to " + "bracket the root. Driver info: %s.", + a, b, vic_run_ref_str); va_end(ap); return(ERROR); } @@ -347,19 +335,16 @@ root_brent(double LowerBound, // Catch ERROR values returned from Function if (fb == ERROR) { - sprintf(ErrorString, "ERROR returned to root_brent on " - "iteration %d: temperature = %.4f. Driver info: %s.", - i + 1, b, vic_run_ref_str); - log_warn("Root Brent is returning an error: %s", ErrorString); + log_warn("iteration %d: temperature = %.4f. Driver info: %s.", + i + 1, b, vic_run_ref_str); va_end(ap); return(ERROR); } } } /* If we get here, there were too many iterations */ - sprintf(ErrorString, "WARNING: %s: too many iterations. Driver info: %s.", - Routine, vic_run_ref_str); - log_warn("Root Brent is returning an error: %s", ErrorString); + log_warn("too many iterations. Driver info: %s.", + vic_run_ref_str); va_end(ap); return(ERROR); } diff --git a/vic/vic_run/src/snow_intercept.c b/vic/vic_run/src/snow_intercept.c index 39cbd54c4..083a02e63 100644 --- a/vic/vic_run/src/snow_intercept.c +++ b/vic/vic_run/src/snow_intercept.c @@ -74,7 +74,7 @@ snow_intercept(double Dt, unsigned short veg_class, double *CanopLayerBnd, double *dryFrac, - atmos_data_struct *atmos, + force_data_struct *force, layer_data_struct *layer, soil_con_struct *soil_con, veg_var_struct *veg_var) @@ -127,13 +127,13 @@ snow_intercept(double Dt, char ErrorString[MAXSTRING]; - AirDens = atmos->density[hidx]; - EactAir = atmos->vp[hidx]; - Press = atmos->pressure[hidx]; - Vpd = atmos->vpd[hidx]; - shortwave = atmos->shortwave[hidx]; + AirDens = force->density[hidx]; + EactAir = force->vp[hidx]; + Press = force->pressure[hidx]; + Vpd = force->vpd[hidx]; + shortwave = force->shortwave[hidx]; if (options.CARBON) { - Catm = atmos->Catm[hidx]; + Catm = force->Catm[hidx]; } else { Catm = MISSING; diff --git a/vic/vic_run/src/snow_melt.c b/vic/vic_run/src/snow_melt.c index d73bfcb7a..ff0b598ab 100644 --- a/vic/vic_run/src/snow_melt.c +++ b/vic/vic_run/src/snow_melt.c @@ -122,13 +122,13 @@ snow_melt(double Le, PackSwq = Ice - SurfaceSwq; /* Calculate cold contents */ - SurfaceCC = CONST_CPFWICE * SurfaceSwq * snow->surf_temp; - PackCC = CONST_CPFWICE * PackSwq * snow->pack_temp; + SurfaceCC = CONST_VCPICE_WQ * SurfaceSwq * snow->surf_temp; + PackCC = CONST_VCPICE_WQ * PackSwq * snow->pack_temp; if (air_temp > 0.0) { SnowFallCC = 0.0; } else { - SnowFallCC = CONST_CPFWICE * SnowFall * air_temp; + SnowFallCC = CONST_VCPICE_WQ * SnowFall * air_temp; } /* Distribute fresh snowfall */ @@ -153,13 +153,13 @@ snow_melt(double Le, SurfaceCC += SnowFallCC; } if (SurfaceSwq > 0.0) { - snow->surf_temp = SurfaceCC / (CONST_CPFWICE * SurfaceSwq); + snow->surf_temp = SurfaceCC / (CONST_VCPICE_WQ * SurfaceSwq); } else { snow->surf_temp = 0.0; } if (PackSwq > 0.0) { - snow->pack_temp = PackCC / (CONST_CPFWICE * PackSwq); + snow->pack_temp = PackCC / (CONST_VCPICE_WQ * PackSwq); } else { snow->pack_temp = 0.0; @@ -415,9 +415,9 @@ snow_melt(double Le, Ice += snow->pack_water; snow->pack_water = 0.0; if (PackSwq > 0.0) { - PackCC = PackSwq * CONST_CPFWICE * snow->pack_temp + + PackCC = PackSwq * CONST_VCPICE_WQ * snow->pack_temp + PackRefreezeEnergy; - snow->pack_temp = PackCC / (CONST_CPFWICE * PackSwq); + snow->pack_temp = PackCC / (CONST_VCPICE_WQ * PackSwq); if (snow->pack_temp > 0.) { snow->pack_temp = 0.; } @@ -456,8 +456,8 @@ snow_melt(double Le, Ice = PackSwq + SurfaceSwq; if (Ice > param.SNOW_MAX_SURFACE_SWE) { - SurfaceCC = CONST_CPFWICE * snow->surf_temp * SurfaceSwq; - PackCC = CONST_CPFWICE * snow->pack_temp * PackSwq; + SurfaceCC = CONST_VCPICE_WQ * snow->surf_temp * SurfaceSwq; + PackCC = CONST_VCPICE_WQ * snow->pack_temp * PackSwq; if (SurfaceSwq > param.SNOW_MAX_SURFACE_SWE) { PackCC += SurfaceCC * (SurfaceSwq - param.SNOW_MAX_SURFACE_SWE) / SurfaceSwq; @@ -474,8 +474,8 @@ snow_melt(double Le, PackSwq -= param.SNOW_MAX_SURFACE_SWE - SurfaceSwq; SurfaceSwq += param.SNOW_MAX_SURFACE_SWE - SurfaceSwq; } - snow->pack_temp = PackCC / (CONST_CPFWICE * PackSwq); - snow->surf_temp = SurfaceCC / (CONST_CPFWICE * SurfaceSwq); + snow->pack_temp = PackCC / (CONST_VCPICE_WQ * PackSwq); + snow->surf_temp = SurfaceCC / (CONST_VCPICE_WQ * SurfaceSwq); } else { PackSwq = 0.0; diff --git a/vic/vic_run/src/solve_snow.c b/vic/vic_run/src/solve_snow.c index 8f48fbb97..2b9d9e52f 100644 --- a/vic/vic_run/src/solve_snow.c +++ b/vic/vic_run/src/solve_snow.c @@ -83,7 +83,7 @@ solve_snow(char overstory, double *CanopLayerBnd, double *dryFrac, dmy_struct *dmy, - atmos_data_struct *atmos, + force_data_struct *force, energy_bal_struct *energy, layer_data_struct *layer, snow_data_struct *snow, @@ -114,12 +114,12 @@ solve_snow(char overstory, month = dmy->month; day_in_year = dmy->day_in_year; - density = atmos->density[hidx]; - longwave = atmos->longwave[hidx]; - pressure = atmos->pressure[hidx]; - shortwave = atmos->shortwave[hidx]; - vp = atmos->vp[hidx]; - vpd = atmos->vpd[hidx]; + density = force->density[hidx]; + longwave = force->longwave[hidx]; + pressure = force->pressure[hidx]; + shortwave = force->shortwave[hidx]; + vp = force->vp[hidx]; + vpd = force->vpd[hidx]; /* initialize moisture variables */ melt = 0.; @@ -217,7 +217,7 @@ solve_snow(char overstory, *UnderStory, band, iveg, month, hidx, veg_class, - CanopLayerBnd, dryFrac, atmos, + CanopLayerBnd, dryFrac, force, layer, soil_con, veg_var); if (ErrorFlag == ERROR) { return (ERROR); diff --git a/vic/vic_run/src/surface_fluxes.c b/vic/vic_run/src/surface_fluxes.c index 8289dcd03..8354ba743 100644 --- a/vic/vic_run/src/surface_fluxes.c +++ b/vic/vic_run/src/surface_fluxes.c @@ -56,7 +56,7 @@ surface_fluxes(bool overstory, double dp, unsigned short iveg, unsigned short veg_class, - atmos_data_struct *atmos, + force_data_struct *force, dmy_struct *dmy, energy_bal_struct *energy, global_param_struct *gp, @@ -261,6 +261,7 @@ surface_fluxes(bool overstory, coverage = snow->coverage; snow_energy = (*energy); soil_energy = (*energy); + iter_soil_energy = (*energy); snow_veg_var = (*veg_var); soil_veg_var = (*veg_var); step_snow = (*snow); @@ -281,7 +282,7 @@ surface_fluxes(bool overstory, if frozen soils are present) ********************************/ - if (snow->swq > 0 || snow->snow_canopy > 0 || atmos->snowflag[NR]) { + if (snow->swq > 0 || snow->snow_canopy > 0 || force->snowflag[NR]) { hidx = 0; step_inc = 1; endhidx = hidx + NF; @@ -381,16 +382,16 @@ surface_fluxes(bool overstory, /* set air temperature and precipitation for this snow band */ - Tair = atmos->air_temp[hidx] + soil_con->Tfactor[band]; - step_prec = atmos->prec[hidx] * soil_con->Pfactor[band]; + Tair = force->air_temp[hidx] + soil_con->Tfactor[band]; + step_prec = force->prec[hidx] * soil_con->Pfactor[band]; // initialize ground surface temperaure Tgrnd = energy->T[0]; // initialize canopy terms Tcanopy = Tair; - VPcanopy = atmos->vp[hidx]; - VPDcanopy = atmos->vpd[hidx]; + VPcanopy = force->vp[hidx]; + VPDcanopy = force->vpd[hidx]; over_iter = 0; tol_over = 999; @@ -411,8 +412,8 @@ surface_fluxes(bool overstory, faparl(CanopLayerBnd, veg_var->LAI, soil_con->AlbedoPar, - atmos->coszen[hidx], - atmos->fdir[hidx], + force->coszen[hidx], + force->fdir[hidx], LAIlayer, faPAR); @@ -422,16 +423,16 @@ surface_fluxes(bool overstory, for (cidx = 0; cidx < options.Ncanopy; cidx++) { if (LAIlayer[cidx] > 1e-10) { veg_var->aPARLayer[cidx] = - (atmos->par[hidx] / + (force->par[hidx] / param.PHOTO_EPAR) * faPAR[cidx] / LAIlayer[cidx]; - veg_var->aPAR += atmos->par[hidx] * faPAR[cidx] / + veg_var->aPAR += force->par[hidx] * faPAR[cidx] / LAIlayer[cidx]; } else { - veg_var->aPARLayer[cidx] = atmos->par[hidx] / + veg_var->aPARLayer[cidx] = force->par[hidx] / param.PHOTO_EPAR * faPAR[cidx] / 1e-10; - veg_var->aPAR += atmos->par[hidx] * faPAR[cidx] / 1e-10; + veg_var->aPAR += force->par[hidx] * faPAR[cidx] / 1e-10; } } free((char*) LAIlayer); @@ -445,8 +446,8 @@ surface_fluxes(bool overstory, step_snow.last_snow, step_snow.surf_water, wind[2], Ls, - atmos->density[hidx], - atmos->vp[hidx], + force->density[hidx], + force->vp[hidx], roughness[2], ref_height[2], step_snow.depth, @@ -569,7 +570,7 @@ surface_fluxes(bool overstory, Nveg, iveg, band, step_dt, hidx, veg_class, &UnderStory, CanopLayerBnd, &dryFrac, - dmy, atmos, &(iter_snow_energy), + dmy, force, &(iter_snow_energy), iter_layer, &(iter_snow), soil_con, &(iter_snow_veg_var)); @@ -622,7 +623,7 @@ surface_fluxes(bool overstory, UnderStory, options.Nnode, Nveg, step_dt, hidx, iveg, (int) overstory, veg_class, - CanopLayerBnd, &dryFrac, atmos, + CanopLayerBnd, &dryFrac, force, dmy, &iter_soil_energy, iter_layer, &(iter_snow), soil_con, @@ -655,7 +656,7 @@ surface_fluxes(bool overstory, iter_snow_energy.NetShortOver, iter_soil_energy.NetShortUnder, iter_aero_resist_veg[1], Tair, - atmos->density[hidx], + force->density[hidx], &iter_soil_energy.AtmosError, &iter_soil_energy.AtmosLatent, &iter_soil_energy.AtmosLatentSub, @@ -738,10 +739,10 @@ surface_fluxes(bool overstory, vic_run_veg_lib[veg_class].CO2Specificity, iter_soil_veg_var.NscaleFactor, Tair, - atmos->shortwave[hidx], + force->shortwave[hidx], iter_soil_veg_var.aPARLayer, soil_con->elevation, - atmos->Catm[hidx], + force->Catm[hidx], CanopLayerBnd, veg_var->LAI, "rs", @@ -795,7 +796,7 @@ surface_fluxes(bool overstory, compute_pot_evap(gp->model_steps_per_day, vic_run_veg_lib[veg_class].rmin, - iter_soil_veg_var.albedo, atmos->shortwave[hidx], + iter_soil_veg_var.albedo, force->shortwave[hidx], iter_soil_energy.NetLongAtmos, vic_run_veg_lib[veg_class].RGL, Tair, VPDcanopy, iter_soil_veg_var.LAI, soil_con->elevation, diff --git a/vic/vic_run/src/vic_run.c b/vic/vic_run/src/vic_run.c index b89c82bda..98b763655 100644 --- a/vic/vic_run/src/vic_run.c +++ b/vic/vic_run/src/vic_run.c @@ -34,7 +34,7 @@ veg_lib_struct *vic_run_veg_lib; * energy and water balance models, as well as frozen soils. ******************************************************************************/ int -vic_run(atmos_data_struct *atmos, +vic_run(force_data_struct *force, all_vars_struct *all_vars, dmy_struct *dmy, global_param_struct *gp, @@ -117,17 +117,17 @@ vic_run(atmos_data_struct *atmos, /* Compute gauge undercatch correction factors - this assumes that the gauge is free of vegetation effects, so gauge correction is constant for the entire grid cell */ - if (options.CORRPREC && atmos->prec[NR] > 0) { - correct_precip(gauge_correction, atmos->wind[NR], gp->wind_h, + if (options.CORRPREC && force->prec[NR] > 0) { + correct_precip(gauge_correction, force->wind[NR], gp->wind_h, soil_con->rough, soil_con->snow_rough); } else { gauge_correction[0] = 1; gauge_correction[1] = 1; } - atmos->out_prec = 0; - atmos->out_rain = 0; - atmos->out_snow = 0; + force->out_prec = 0; + force->out_rain = 0; + force->out_snow = 0; // Convert LAI from global to local for (iveg = 0; iveg < Nveg; iveg++) { @@ -240,7 +240,7 @@ vic_run(atmos_data_struct *atmos, *************************************/ /* Initialize wind speeds */ - tmp_wind[0] = atmos->wind[NR]; + tmp_wind[0] = force->wind[NR]; tmp_wind[1] = MISSING; tmp_wind[2] = MISSING; @@ -296,7 +296,7 @@ vic_run(atmos_data_struct *atmos, vic_run_veg_lib[veg_class].NscaleFlag, veg_con[iveg].CanopLayerBnd, veg_var[iveg][band].LAI, - atmos->coszen[NR], + force->coszen[NR], veg_var[iveg][band].NscaleFactor); // TBD: move this outside of vic_run() if (dmy->day_in_year == 1) { @@ -333,7 +333,7 @@ vic_run(atmos_data_struct *atmos, &snow_inflow[band], tmp_wind, veg_con[iveg].root, options.Nlayer, Nveg, band, dp, - iveg, veg_class, atmos, dmy, + iveg, veg_class, force, dmy, &(energy[iveg][band]), gp, &(cell[iveg][band]), &(snow[iveg][band]), @@ -345,11 +345,11 @@ vic_run(atmos_data_struct *atmos, return (ERROR); } - atmos->out_prec += + force->out_prec += out_prec[band * 2] * Cv * soil_con->AreaFract[band]; - atmos->out_rain += + force->out_rain += out_rain[band * 2] * Cv * soil_con->AreaFract[band]; - atmos->out_snow += + force->out_snow += out_snow[band * 2] * Cv * soil_con->AreaFract[band]; /******************************************************** @@ -438,10 +438,10 @@ vic_run(atmos_data_struct *atmos, lake_var->baseflow_in = (sum_baseflow * lake_con->rpercent + wetland_baseflow) * soil_con->cell_area / MM_PER_M; // m3 - lake_var->channel_in = atmos->channel_in[NR] * soil_con->cell_area / + lake_var->channel_in = force->channel_in[NR] * soil_con->cell_area / MM_PER_M; // m3 - lake_var->prec = atmos->prec[NR] * lake_var->sarea / MM_PER_M; // m3 - rainonly = calc_rainonly(atmos->air_temp[NR], atmos->prec[NR], + lake_var->prec = force->prec[NR] * lake_var->sarea / MM_PER_M; // m3 + rainonly = calc_rainonly(force->air_temp[NR], force->prec[NR], param.SNOW_MAX_SNOW_TEMP, param.SNOW_MIN_RAIN_TEMP); if ((int) rainonly == ERROR) { @@ -452,19 +452,19 @@ vic_run(atmos_data_struct *atmos, Solve the energy budget for the lake. **********************************************************************/ - snowprec = gauge_correction[SNOW] * (atmos->prec[NR] - rainonly); + snowprec = gauge_correction[SNOW] * (force->prec[NR] - rainonly); rainprec = gauge_correction[SNOW] * rainonly; Cv = veg_con[iveg].Cv * lakefrac; - atmos->out_prec += (snowprec + rainprec) * Cv; - atmos->out_rain += rainprec * Cv; - atmos->out_snow += snowprec * Cv; - - ErrorFlag = solve_lake(snowprec, rainprec, atmos->air_temp[NR], - atmos->wind[NR], atmos->vp[NR] / PA_PER_KPA, - atmos->shortwave[NR], atmos->longwave[NR], - atmos->vpd[NR] / PA_PER_KPA, - atmos->pressure[NR] / PA_PER_KPA, - atmos->density[NR], lake_var, + force->out_prec += (snowprec + rainprec) * Cv; + force->out_rain += rainprec * Cv; + force->out_snow += snowprec * Cv; + + ErrorFlag = solve_lake(snowprec, rainprec, force->air_temp[NR], + force->wind[NR], force->vp[NR] / PA_PER_KPA, + force->shortwave[NR], force->longwave[NR], + force->vpd[NR] / PA_PER_KPA, + force->pressure[NR] / PA_PER_KPA, + force->density[NR], lake_var, *soil_con, gp->dt, gp->wind_h, *dmy, fraci); if (ErrorFlag == ERROR) {