diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..264377de --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,35 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "OctaveDebugger", + "request": "launch", + "name": "Octave: Execute selected m-file", + "program": "${file}", + "octave": "octave.bat --no-gui --interactive --silent", + "autoTerminate": true, + "sourceFolder": "${workspaceFolder}/src" + }, + { + "type": "OctaveDebugger", + "request": "launch", + "name": "Octave: Debug STEMMUS-SCOPE", + "program": "debug_Octave.m", + "octave": "octave.bat --no-gui --interactive --silent", + "autoTerminate": true, + "sourceFolder": "${workspaceFolder}/src" + }, + { + "type": "OctaveDebugger", + "request": "launch", + "name": "Octave: Interactive", + "program": "", + "octave": "octave.bat --no-gui --interactive --silent", + "autoTerminate": false, + "sourceFolder": "${workspaceFolder}" + }, + ] +} \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e94694e2..b53b92be 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,15 +1,17 @@ # Contributing guidelines -We welcome any kind of contributions to our software, from simple -comment or question to a full [pull -request](https://help.github.com/articles/about-pull-requests/). Please +This repository includes the MATLAB source code of the STEMMUS-SCOPE model. We welcome any +kind of contributions to our software, from simple comments or questions to a full +[pull request](https://help.github.com/articles/about-pull-requests/). Please read and follow our contributing guidelines. ## Contributing via GitHub -Note when we want to work with `STEMMUS_SCOPE` repository on a new computer for -the first time, we need to configure a few things following steps 1 to 4 below. +If you want to work with the `STEMMUS_SCOPE` repository for the first time, or on a new computer, +you need to configure a few things following steps 1 through 5 below. +
+ Steps 1 to 5 ### 1. Enable two-factor authentication It is strongly recommended using two-factor authentication. Here is the link of @@ -132,10 +134,24 @@ git clone git@github.com:EcoExtreML/STEMMUS_SCOPE.git Now a new GitHub folder `STEMMUS_SCOPE` is created in your `HOME` directory. > In this command, we clone the repository using `ssh` option. As we set the ssh -connection in [**Step 2**](#2-set-ssh-connection), this command here does not ask for our user name and -password. +connection in [**Step 2**](#2-set-ssh-connection), this command here does not +ask for our user name and password. ### 5. Collaborate using GitHub To know about the most common Git commands, follow the guides -[here](https://hackmd.io/B4v6KwsBRzG-akLDF8e5pg). \ No newline at end of file +[here](https://hackmd.io/B4v6KwsBRzG-akLDF8e5pg). +
+ +## Development of the MATLAB source of STEMMUS_SCOPE model + +To contribute to the STEMMUS_SCOPE model, you need access to the model source code that is stored in the repository [STEMMUS_SCOPE](https://github.com/EcoExtreML/STEMMUS_SCOPE). You also need a MATLAB license. MATLAB `2021a` is installed on +[Snellius]((https://servicedesk.surfsara.nl/wiki/display/WIKI/Snellius)) and [CRIB](https://crib.utwente.nl/), see [this instruction](https://pystemmusscope.readthedocs.io/en/latest/contributing_link.html#development-of-stemmus-scope-model). + +## Create an executable file of STEMMUS_SCOPE + +See the [exe readme](./exe/README.md). + +## Follow MATLAB style guidelines + +Please follow the style introduced in [MATLAB Guidelines 2.0, Richard Johnson](http://cnl.sogang.ac.kr/cnlab/lectures/programming/matlab/Richard_Johnson-MatlabStyle2_book.pdf). \ No newline at end of file diff --git a/Octave_instructions.md b/Octave_instructions.md new file mode 100644 index 00000000..5cdd8e28 --- /dev/null +++ b/Octave_instructions.md @@ -0,0 +1,48 @@ +# Using STEMMUS-SCOPE with GNU Octave +The downloads can be found here +https://octave.org/download + +TODO: installation of octave on linux +After installation, launch octave and install the following Octave packages: +`pkg install -forge netcdf` +`pkg install -forge statistics` + +For off-line installation, first, download the packages [netcdf](https://octave.sourceforge.io/netcdf/index.html), [io](https://octave.sourceforge.io/io/index.html) and [statistics](https://octave.sourceforge.io/statistics/index.html). Then, launch octave and run: + +`pkg install netcdf-1.0.16.tar.gz` +`pkg install io-2.6.4.tar.gz` +`pkg install statistics-1.4.3.tar.gz` + +### VS Code setup +Add Octave to path, e.g. for (64-bit) Windows add the following folders: +`C:\Program Files\GNU Octave\Octave-7.1.0\mingw64\bin` +`C:\Program Files\GNU Octave\Octave-7.1.0\usr\bin` + +Add the extensions +`Octave Debugger` by Paulo Silva https://marketplace.visualstudio.com/items?itemName=paulosilva.vsc-octave-debugger +This allows to run Octave in the VS Code debugger. +The debugger configurations are included in `/.vscode/launch.json` + +`Octave Hacking` by Andrew Janke https://marketplace.visualstudio.com/items?itemName=apjanke.octave-hacking +This adds syntax highlighting and formatting. + +### Running STEMMUS-SCOPE in Octave +It is possible to run STEMMUS-SCOPE from the command line with the following setup: +`octave.bat --no-gui --interactive --silent --eval "STEMMUS_SCOPE_exe('path_to_config_file')"` + +On a Unix system, use `octave` instead of `octave.bat`. +### Developing STEMMUS-SCOPE in Octave +Open the `run_Octave.m` file, either in VS Code or the Octave GUI. + +#### Octave GUI +Set the workspace to the `STEMMUS_SCOPE/src` folder, and open the `run_Octave.m` file. +Here you can set the config file that should be used, and then run the file. + +#### VS Code +While having the `STEMMUS_SCOPE` folder as the workspace, open the debugger and select `Octave: Debug STEMMUS-SCOPE`. +Start the debugger to run (and debug) the model. + +In the `run_Octave.m` file you can set the config file that should be used. + + + diff --git a/README.md b/README.md index 640531e6..b1193018 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@ # STEMMUS_SCOPE -`STEMMUS_SCOPE` serves as an integrated code of SCOPE and STEMMUS. +Integrated code of SCOPE and STEMMUS. -SCOPE is a radiative transfer and energy balance model, and STEMMUS model is a two-phase mass and heat transfer model. For more information about the coupling between these two models, please check [this reference](https://gmd.copernicus.org/articles/14/1379/2021/). +SCOPE is a radiative transfer and energy balance model, and STEMMUS model is a two-phase mass and heat transfer model. For more information about the coupling between these two models, please check [this reference](https://gmd.copernicus.org/articles/14/1379/2021/). Before running the model, you need to prepare input data and a configuration file. This can be done using the python package +[PyStemmusScope](https://pystemmusscope.readthedocs.io). Logo (by Zeng & Su, 2021) @@ -41,6 +42,3 @@ have a look at the [contribution guidelines](CONTRIBUTING.md). ## How to cite us [![RSD](https://img.shields.io/badge/rsd-ecoextreml-00a3e3.svg)](https://research-software-directory.org/projects/ecoextreml) - - -More information will follow soon. diff --git a/config_file_crib.txt b/config_file_crib.txt index c5ce39cb..0d9c9dde 100644 --- a/config_file_crib.txt +++ b/config_file_crib.txt @@ -1,7 +1,14 @@ +WorkDir=/home/jovyan/private/ SoilPropertyPath=/data/shared/EcoExtreML/STEMMUS_SCOPEv1.0.0/input/SoilProperty/ -InputPath=/data/shared/EcoExtreML/STEMMUS_SCOPEv1.0.0/input/ -OutputPath=/data/shared/EcoExtreML/STEMMUS_SCOPEv1.0.0/output/ ForcingPath=/data/shared/EcoExtreML/STEMMUS_SCOPEv1.0.0/input/Plumber2_data/ ForcingFileName=AU-DaS_2010-2017_OzFlux_Met.nc +directional=/data/shared/EcoExtreML/STEMMUS_SCOPEv1.0.0/input/directional/ +fluspect_parameters=/data/shared/EcoExtreML/STEMMUS_SCOPEv1.0.0/input/fluspect_parameters/ +leafangles=/data/shared/EcoExtreML/STEMMUS_SCOPEv1.0.0/input/leafangles/ +radiationdata=/data/shared/EcoExtreML/STEMMUS_SCOPEv1.0.0/input/radiationdata/ +soil_spectrum=/data/shared/EcoExtreML/STEMMUS_SCOPEv1.0.0/input/soil_spectrum/ +input_data=/data/shared/EcoExtreML/STEMMUS_SCOPEv1.0.0/input/input_data.xlsx InitialConditionPath=/data/shared/EcoExtreML/STEMMUS_SCOPEv1.0.0/input/Initial_condition/ -DurationSize=NA +NumberOfTimeSteps=NA +InputPath= +OutputPath= \ No newline at end of file diff --git a/config_file_snellius.txt b/config_file_snellius.txt deleted file mode 100644 index 7e7f7fae..00000000 --- a/config_file_snellius.txt +++ /dev/null @@ -1,8 +0,0 @@ -SoilPropertyPath=/projects/0/einf2480/model_parameters/soil_property/ -InputPath=/scratch-shared/ecoextreml/stemmus_scope/input/ -OutputPath=/scratch-shared/ecoextreml/stemmus_scope/output/ -ForcingPath=/projects/0/einf2480/forcing/plumber2_data/ -ForcingFileName=FI-Hyy_1996-2014_FLUXNET2015_Met.nc -VegetationPropertyPath=/projects/0/einf2480/model_parameters/vegetation_property/ -InitialConditionPath=/projects/0/einf2480/soil_initialcondition/ -DurationSize=17520 \ No newline at end of file diff --git a/config_file_template.txt b/config_file_template.txt new file mode 100644 index 00000000..a78c0cb4 --- /dev/null +++ b/config_file_template.txt @@ -0,0 +1,14 @@ +WorkDir=/path_to_working_directory/ +SoilPropertyPath=/path_to_soil_property_data/ +ForcingPath=/path_to_forcing_data/ +ForcingFileName=AU-DaS_2010-2017_OzFlux_Met.nc +directional=/path_to_directional_data/ +fluspect_parameters=/path_to_fluspect_parameters_data/ +leafangles=/path_to_leafangles_data/ +radiationdata=/path_to_radiation_data/ +soil_spectrum=/path_to_soil_spectra_data/ +InitialConditionPath=/path_to_soil_initial_condition_data/ +input_data=/path_to_input_data.xlsx_file/ +NumberOfTimeSteps=NA +InputPath=will_be_created_under_WorkDir +OutputPath=will_be_created_under_WorkDir diff --git a/exe/STEMMUS_SCOPE b/exe/STEMMUS_SCOPE deleted file mode 100755 index cd97ea73..00000000 Binary files a/exe/STEMMUS_SCOPE and /dev/null differ diff --git a/pull_request_template.md b/pull_request_template.md index 8aa1b1a5..de5657b4 100644 --- a/pull_request_template.md +++ b/pull_request_template.md @@ -5,9 +5,9 @@ Please add a description of the changes proposed in the pull request. Please re-generate exe file if matlab codes are changed. About how to create an exe file, see [exe readme](https://github.com/EcoExtreML/STEMMUS_SCOPE/blob/main/exe/README.md). ### Checklist -- [ ] Add a refernce to related issues. +- [ ] Add a reference to related issues. - [ ] @mentions of the person or team responsible for reviewing proposed changes. - [ ] This pull request has a descriptive title. -- [ ] Code is written according to the [guidelines].(http://cnl.sogang.ac.kr/cnlab/lectures/programming/matlab/Richard_Johnson-MatlabStyle2_book.pdf) +- [ ] Code is written according to the [guidelines](http://cnl.sogang.ac.kr/cnlab/lectures/programming/matlab/Richard_Johnson-MatlabStyle2_book.pdf). - [ ] Documentation is available. - [ ] Model runs successfully. diff --git a/run_model_on_snellius/README.md b/run_model_on_snellius/README.md new file mode 100644 index 00000000..92cc0bd6 --- /dev/null +++ b/run_model_on_snellius/README.md @@ -0,0 +1,9 @@ +## Run STEMMUS_SCOPE on Snellius + +This folder contains files that are needed for running `STEMMUS_SCOPE` on Snellius: + +- `run_stemmus_scope_snellius.sh` is the batch script for job submission +- `run_model.py` is the python script for executing the workflow +- `config_file_snellius.txt` is the model config file + +Before submitting the job via `sbatch run_stemmus_scope_snellius.sh`, make sure that conda environment `pystemmusscope` is created, see the [environment file](https://github.com/EcoExtreML/STEMMUS_SCOPE_Processing/blob/main/environment.yml). Also, set the `WorkDir` and `NumberOfTimeSteps` in the model config file e.g. `config_file_snellius.txt`. diff --git a/run_model_on_snellius/config_file_snellius.txt b/run_model_on_snellius/config_file_snellius.txt new file mode 100644 index 00000000..f1bcdb3a --- /dev/null +++ b/run_model_on_snellius/config_file_snellius.txt @@ -0,0 +1,14 @@ +WorkDir=/scratch-shared/ecoextreml/stemmus_scope/ +SoilPropertyPath=/projects/0/einf2480/model_parameters/soil_property/ +ForcingPath=/projects/0/einf2480/forcing/plumber2_data/ +ForcingFileName=FI-Hyy_1996-2014_FLUXNET2015_Met.nc +directional=/projects/0/einf2480/model_parameters/vegetation_property/directional/ +fluspect_parameters=/projects/0/einf2480/model_parameters/vegetation_property/fluspect_parameters/ +leafangles=/projects/0/einf2480/model_parameters/vegetation_property/leafangles/ +radiationdata=/projects/0/einf2480/model_parameters/vegetation_property/radiationdata/ +soil_spectrum=/projects/0/einf2480/model_parameters/vegetation_property/soil_spectrum/ +input_data=/projects/0/einf2480/model_parameters/vegetation_property/input_data.xlsx +InitialConditionPath=/projects/0/einf2480/soil_initialcondition/ +NumberOfTimeSteps=17520 +InputPath= +OutputPath= \ No newline at end of file diff --git a/exe/README.md b/run_model_on_snellius/exe/README.md similarity index 100% rename from exe/README.md rename to run_model_on_snellius/exe/README.md diff --git a/run_model_on_snellius/exe/STEMMUS_SCOPE b/run_model_on_snellius/exe/STEMMUS_SCOPE new file mode 100755 index 00000000..112ffb55 Binary files /dev/null and b/run_model_on_snellius/exe/STEMMUS_SCOPE differ diff --git a/exe/build_stemmus_scope_exe.sh b/run_model_on_snellius/exe/build_stemmus_scope_exe.sh similarity index 100% rename from exe/build_stemmus_scope_exe.sh rename to run_model_on_snellius/exe/build_stemmus_scope_exe.sh diff --git a/run_model_on_snellius/run_model.py b/run_model_on_snellius/run_model.py new file mode 100644 index 00000000..6306216c --- /dev/null +++ b/run_model_on_snellius/run_model.py @@ -0,0 +1,89 @@ +"""Run STEMMUS_SCOPE on Snellius. +This script is used in a job submission file to loop over forcing files and run +StemmusScope model on a HPC cluster using slurm workload manager. In order to +run the model, PyStemmusScope should be installed, see +https://pystemmusscope.readthedocs.io/. +""" + +import argparse +from pathlib import Path +from PyStemmusScope import StemmusScope +from PyStemmusScope import save + + +def run_model(ncfile_index, job_id): + """Workflow executer. + + Run StemmusScope model with PyStemmusScope. + """ + path_to_config_file = "./config_file_snellius.txt" + path_to_exe_file = "./exe/STEMMUS_SCOPE" + + # create an instance of the model + model = StemmusScope(config_file=path_to_config_file, exe_file=path_to_exe_file) + + # get the forcing file + forcing_filenames_list = [ + file.name for file in Path(model.config["ForcingPath"]).iterdir() + ] + # note that index starts from 1 from bash input, so ncfile_index-1 + nc_file = forcing_filenames_list[ncfile_index - 1] + station_name = nc_file.split("_")[0] + + # feed model with the correct forcing file + config_path = model.setup(ForcingFileName=nc_file) + + # run model + model_log = model.run() + + # save output in netcdf format + required_netcdf_variables = "../utils/csv_to_nc/required_netcdf_variables.csv" + netcdf_file_name = save.to_netcdf(config_path, required_netcdf_variables) + + slurm_log_msg = [ + f"Input path is {model.config['InputPath']}", + f"Output path is {model.config['OutputPath']}", + f"model log is {model_log}", + f"model outputs are in {netcdf_file_name}", + ] + + slurm_file_name = ( + Path(model.config["OutputPath"]) + / f"slurm_{job_id}_{ncfile_index}_{station_name}.out" + ) + + # create slurm log + slurm_log(slurm_file_name, slurm_log_msg) + + +def slurm_log(slurm_file_name, slurm_log_msg): + """Create slurm log file for the submitted job.""" + with open(slurm_file_name, "w+", encoding="utf-8") as file: + for line in slurm_log_msg: + file.write(line) + file.write("\n") + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + + parser.add_argument( + "-n", + "--ncfile_index", + required=True, + type=int, + default=0, + help="index of netCDF file", + ) + parser.add_argument( + "-j", + "--job_id", + required=True, + type=int, + default=0, + help="slurm job id from snellius", + ) + # get arguments + args = parser.parse_args() + + run_model(args.ncfile_index, args.job_id) diff --git a/run_model_on_snellius/run_stemmus_scope_snellius.sh b/run_model_on_snellius/run_stemmus_scope_snellius.sh new file mode 100644 index 00000000..50309996 --- /dev/null +++ b/run_model_on_snellius/run_stemmus_scope_snellius.sh @@ -0,0 +1,48 @@ +#!/bin/bash +# This is a batch script for Snellius at Surf +# usage: cd STEMMUS_SCOPE/run_model_on_snellius; sbatch run_stemmus_scope_snellius.sh + +# SLURM settings +#SBATCH -J stemmus_scope +#SBATCH -t 00:10:00 +#SBATCH --nodes=1 +#SBATCH --ntasks=32 +#SBATCH -p thin +#SBATCH --output=./slurm_%j.out +#SBATCH --error=./slurm_%j.out + +### 1. Load module needed to run the model (no need for license) +module load 2021 + +### This is for Matlab +module load MCR/R2021a.3 + +### python environment pystemmusscope is needed +### see https://pystemmusscope.readthedocs.io +. ~/mamba/bin/activate pystemmusscope + +### 2. Some security: stop script on error and undefined variables +set -euo pipefail + +### 3. Run parallel loop +# number of cores on a shared node +ncores=32 + +# number of forcing files +nfiles=170 + +# some indices for loop +k=0 +i=0 + +for k in `seq 0 5`; do + for j in `seq 1 $ncores` ; do + ( + i=$(( ncores * k + j )) + if [[ $i -le $nfiles ]]; then + python run_model.py -n $i -j ${SLURM_JOB_ID} + fi + )& + done + wait +done \ No newline at end of file diff --git a/run_stemmus_scope_snellius.sh b/run_stemmus_scope_snellius.sh deleted file mode 100644 index 84907798..00000000 --- a/run_stemmus_scope_snellius.sh +++ /dev/null @@ -1,111 +0,0 @@ -#!/bin/bash -# This is a batch script for Snellius at Surf -# usage: cd STEMMUS_SCOPE; mkdir -p slurm; sbatch run_stemmus_scope_snellius.sh - -# SLURM settings -#SBATCH -J stemmus_scope -#SBATCH -t 00:10:00 -#SBATCH --nodes=1 -#SBATCH --ntasks=32 -#SBATCH -p thin -#SBATCH --output=./slurm/slurm_%j.out -#SBATCH --error=./slurm/slurm_%j.out - -### 1. Load module needed to run the model (no need for license) -module load 2021 - -### This is for Matlab -module load MCR/R2021a.3 - -### python environment stemmus is needed to convert csv files to nc files -### see utils/csv_to_nc/README.md -. ~/mamba/bin/activate stemmus - -### 2. Some security: stop script on error and undefined variables -set -euo pipefail - -### 3. Create a function to loop over -loop_func() { - - ### 3.2 Get paths from a config file - config="config_file_snellius.txt" - source $config - - ### 3.3 loop over forcing file using i that is the function input argument - i=$1 - ncfiles=("$(echo "$ForcingPath" | tr -d '\r')"*.nc) - - ### -1 is needed because ncfiles array starts from 0 - file=${ncfiles[$i-1]} - - start_time=$(date +%s) - base_name=$(basename ${file}) - station_name="${base_name%%_*}" - timestamp=$(date +"%Y-%m-%d-%H%M") - - ### 3.4 create input directories, - work_dir="$(echo "$InputPath" | tr -d '\r')${station_name}_${timestamp}/" - mkdir -p $work_dir - - ### 3.5 copy files to work_dir, - vegetation_path="$(echo "$VegetationPropertyPath" | tr -d '\r')" - cp -r $vegetation_path/* $work_dir - - ### 3.6 update config file for ForcingFileName and InputPath, - ### !due to "/" in work_dir, the sed uses "|" instead of "/" - station_config="${work_dir}${station_name}_${timestamp}_config.txt" - sed -e "s/ForcingFileName=.*$/ForcingFileName=$base_name/g" \ - -e "s|InputPath=.*$|InputPath=$work_dir|g" <$config >$station_config - - ### 3.7 Add info to a std_out file - std_out="./slurm/slurm_${SLURM_JOB_ID}_${i}_${station_name}.out" - echo " - SoilPropertyPath is $SoilPropertyPath - InputPath is $InputPath - OutputPath is $OutputPath - ForcingPath is $ForcingPath - VegetationPropertyPath is $VegetationPropertyPath - WorkDir is $work_dir - ForcingFileName is $base_name" > $std_out - - ### 3.8 run the model and store the time and model log info - ### set matlab log dir to slurm, otherwise java.log files are created in user home dir - export MATLAB_LOG_DIR=./slurm - exe/STEMMUS_SCOPE $station_config >> $std_out - - end_time=$(date +%s) - run_time=$(expr $end_time - $start_time) - - ## 3.9 Add some information to slurm*.out file later will be used. - completed="COMPLETED" - echo "Run is $completed. Model run time is $run_time s." >> $std_out - - ## 3.10 Convert csv files to a nc file, if run is completed - if [[ -v completed ]]; - then - python utils/csv_to_nc/generate_netcdf_files.py --config_file $station_config --variable_file utils/csv_to_nc/Variables_will_be_in_NetCDF_file.csv - fi -} - -### 5. Run parallel loop -# number of cores on a shared node -ncores=32 - -# number of forcing files -nfiles=170 - -# some indices for loop -k=0 -i=0 - -for k in `seq 0 5`; do - for j in `seq 1 $ncores` ; do - ( - i=$(( ncores * k + j )) - if [[ $i -le $nfiles ]]; then - loop_func $i - fi - )& - done - wait -done \ No newline at end of file diff --git a/src/+io/bin_to_csv.m b/src/+io/bin_to_csv.m index 70b6bd0c..43575199 100644 --- a/src/+io/bin_to_csv.m +++ b/src/+io/bin_to_csv.m @@ -1,138 +1,142 @@ function bin_to_csv(fnames, V, vmax, n_col, ns, options, SoilLayer) -%% flu -flu_names = {'simulation_number','nu_iterations','year','DoY',... - 'Rntot','lEtot','Htot', ... - 'Rnctot','lEctot','Hctot','Actot', ... - 'Rnstot','lEstot','Hstot','Gtot','Resp', ... - 'aPAR','aPAR_Cab','aPAR/rad.PAR','aPAR_Wm2','PAR','rad.Eoutf','rad.Eoutf./fluxes.aPAR_Wm2','Trap','Evap','ET','GPP','NEE'}; -flu_units = {'','','','', ... - 'W m-2','W m-2','W m-2',... - 'W m-2','W m-2','W m-2','umol m-2 s-1',... - 'W m-2',' W m-2','W m-2','W m-2','umol m-2 s-1',... - 'umol m-2 s-1',' umol m-2 s-1','umol m-2 s-1','W m-2','umol m-2 s-1','W m-2','W m-2','cm s-1','cm s-1','cm s-1','Kg m-2 s-1','Kg m-2 s-1'}; -write_output(flu_names, flu_units, fnames.flu_file, n_col.flu, ns) - -%% surftemp -surftemp_names = {'simulation_number','year','DoY',... - 'Ta','Ts(1)','Ts(2)','Tcave','Tsave'}; -surftemp_units = {'','','', ... - 'C','C','C','C','C'}; -write_output(surftemp_names, surftemp_units, fnames.surftemp_file, n_col.surftemp, ns) - -%% aerodyn -aerodyn_names = {'simulation_number','year','DoY',... - 'raa','rawc','raws','ustar','rac','ras'}; -aerodyn_units = {'','','', ... - 's m-1','s m-1','s m-1', 'm s-1','s m-1','s m-1'}; -write_output(aerodyn_names, aerodyn_units, fnames.aerodyn_file, n_col.aerodyn, ns) - -%% radiation -radiation_names = {'simulation_number','year','DoY',... - 'Rin','Rli','HemisOutShort','HemisOutLong','HemisOutTot','Netshort','Netlong','Rntot'}; -radiation_units = {'','','', ... - 'W m-2','W m-2','W m-2', 'W m-2','W m-2','W m-2','W m-2','W m-2'}; -write_output(radiation_names, radiation_units, fnames.radiation_file, n_col.radiation, ns) - -%% Get soil layer information -depth = string(SoilLayer.depth); -thickness = string(SoilLayer.thickness); - -%% Prepare soil moisture data -Sim_Theta_names = [depth; thickness]; -Sim_Theta_units = repelem({'m-3 m-3'}, length(depth)); -write_output(Sim_Theta_names, Sim_Theta_units, ... -fnames.Sim_Theta_file, n_col.Sim_Theta, ns, true) - -%% Prepare soil temperature data -Sim_Temp_names = [depth; thickness]; -Sim_Temp_units = repelem({'oC'}, length(depth)); -write_output(Sim_Temp_names, Sim_Temp_units, ... -fnames.Sim_Temp_file, n_col.Sim_Temp, ns, true) - -%% spectrum (added on 19 September 2008) - -spectrum_hemis_optical_names = {'hemispherically integrated radiation spectrum'}; -spectrum_hemis_optical_units = {'W m-2 um-1'}; -write_output(spectrum_hemis_optical_names, spectrum_hemis_optical_units, fnames.spectrum_hemis_optical_file, n_col.spectrum_hemis_optical, ns, true) - -spectrum_obsdir_optical_names = {'radiance spectrum in observation direction'}; -spectrum_obsdir_optical_units = {'W m-2 sr-1 um-1'}; -write_output(spectrum_obsdir_optical_names, spectrum_obsdir_optical_units, fnames.spectrum_obsdir_optical_file, n_col.spectrum_obsdir_optical, ns, true) - -if options.calc_ebal - write_output({'thermal BlackBody emission spectrum in observation direction'}, {'W m-2 sr-1 um-1'}, ... - fnames.spectrum_obsdir_BlackBody_file, n_col.spectrum_obsdir_BlackBody, ns, true) - if options.calc_planck - write_output({'thermal emission spectrum in hemispherical direction'}, {'W m-2 sr-1 um-1'}, ... - fnames.spectrum_hemis_thermal_file, n_col.spectrum_hemis_thermal, ns, true) - write_output({'thermal emission spectrum in observation direction'}, {'W m-2 sr-1 um-1'}, ... - fnames.spectrum_obsdir_thermal_file, n_col.spectrum_obsdir_thermal, ns, true) - end -end - write_output({'irradiance'}, {'W m-2 um-1'}, ... - fnames.irradiance_spectra_file, n_col.irradiance_spectra, ns, true) - write_output({'reflectance'}, {'fraction of radiation in observation direction *pi / irradiance'}, ... - fnames.reflectance_file, n_col.reflectance, ns, true) -%% input and parameter values (added June 2012) -%write_output(fnames.pars_and_input_file, true) -%write_output(fnames.pars_and_input_short_file, true) -%% Optional Output -if options.calc_vert_profiles - write_output({'Fraction leaves in the sun, fraction of observed, fraction of observed&visible per layer'}, {'rows: simulations or time steps, columns: layer numbers'}, ... - fnames.gap_file, n_col.gap, ns, true) - write_output({'aPAR per leaf layer'}, {'umol m-2 s-1'}, ... - fnames.layer_aPAR_file, n_col.layer_aPAR, ns, true) - write_output({'aPAR by Cab per leaf layer'}, {'umol m-2 s-1'}, ... - fnames.layer_aPAR_Cab_file, n_col.layer_aPAR_Cab, ns, true) + %% flu + flu_names = {'simulation_number','nu_iterations','year','DoY',... + 'Rntot','lEtot','Htot', ... + 'Rnctot','lEctot','Hctot','Actot', ... + 'Rnstot','lEstot','Hstot','Gtot','Resp', ... + 'aPAR','aPAR_Cab','aPAR/rad.PAR','aPAR_Wm2','PAR','rad.Eoutf','rad.Eoutf./fluxes.aPAR_Wm2','Trap','Evap','ET','GPP','NEE'}; + flu_units = {'','','','', ... + 'W m-2','W m-2','W m-2',... + 'W m-2','W m-2','W m-2','umol m-2 s-1',... + 'W m-2',' W m-2','W m-2','W m-2','umol m-2 s-1',... + 'umol m-2 s-1',' umol m-2 s-1','umol m-2 s-1','W m-2','umol m-2 s-1','W m-2','W m-2','cm s-1','cm s-1','cm s-1','Kg m-2 s-1','Kg m-2 s-1'}; + write_output(flu_names, flu_units, fnames.flu_file, n_col.flu, ns) + + %% surftemp + surftemp_names = {'simulation_number','year','DoY',... + 'Ta','Ts(1)','Ts(2)','Tcave','Tsave'}; + surftemp_units = {'','','', ... + 'C','C','C','C','C'}; + write_output(surftemp_names, surftemp_units, fnames.surftemp_file, n_col.surftemp, ns) + + %% aerodyn + aerodyn_names = {'simulation_number','year','DoY',... + 'raa','rawc','raws','ustar','rac','ras'}; + aerodyn_units = {'','','', ... + 's m-1','s m-1','s m-1', 'm s-1','s m-1','s m-1'}; + write_output(aerodyn_names, aerodyn_units, fnames.aerodyn_file, n_col.aerodyn, ns) + + %% radiation + radiation_names = {'simulation_number','year','DoY',... + 'Rin','Rli','HemisOutShort','HemisOutLong','HemisOutTot','Netshort','Netlong','Rntot'}; + radiation_units = {'','','', ... + 'W m-2','W m-2','W m-2', 'W m-2','W m-2','W m-2','W m-2','W m-2'}; + write_output(radiation_names, radiation_units, fnames.radiation_file, n_col.radiation, ns) + + %% Get soil layer information + depth = arrayfun(@(x) num2str(x), SoilLayer.depth, "UniformOutput", false); + thickness = arrayfun(@(x) num2str(x), SoilLayer.thickness, "UniformOutput", false); + + %% Prepare soil moisture data + Sim_Theta_names = [depth; thickness]; + Sim_Theta_units = repelem({'m-3 m-3'}, length(depth)); + write_output(Sim_Theta_names, Sim_Theta_units, ... + fnames.Sim_Theta_file, n_col.Sim_Theta, ns, true) + + %% Prepare soil temperature data + Sim_Temp_names = [depth; thickness]; + Sim_Temp_units = repelem({'oC'}, length(depth)); + write_output(Sim_Temp_names, Sim_Temp_units, ... + fnames.Sim_Temp_file, n_col.Sim_Temp, ns, true) + + %% spectrum (added on 19 September 2008) + + spectrum_hemis_optical_names = {'hemispherically integrated radiation spectrum'}; + spectrum_hemis_optical_units = {'W m-2 um-1'}; + write_output(spectrum_hemis_optical_names, spectrum_hemis_optical_units, fnames.spectrum_hemis_optical_file, n_col.spectrum_hemis_optical, ns, true) + + spectrum_obsdir_optical_names = {'radiance spectrum in observation direction'}; + spectrum_obsdir_optical_units = {'W m-2 sr-1 um-1'}; + write_output(spectrum_obsdir_optical_names, spectrum_obsdir_optical_units, fnames.spectrum_obsdir_optical_file, n_col.spectrum_obsdir_optical, ns, true) + if options.calc_ebal - write_output({'leaf temperature of sunlit leaves, shaded leaves, and weighted average leaf temperature per layer'}, {'^oC ^oC ^oC'}, ... - fnames.leaftemp_file, n_col.leaftemp, ns, true) - write_output({'sensible heat flux per layer'}, {'W m-2'}, ... - fnames.layer_H_file, n_col.layer_H, ns, true) - write_output({'latent heat flux per layer'}, {'W m-2'}, ... - fnames.layer_LE_file, n_col.layer_LE, ns, true) - write_output({'photosynthesis per layer'}, {'umol m-2 s-1'}, ... - fnames.layer_A_file, n_col.layer_A, ns, true) - write_output({'average NPQ = 1-(fm-fo)/(fm0-fo0), per layer'}, {''}, ... - fnames.layer_NPQ_file, n_col.layer_NPQ, ns, true) - write_output({'net radiation per leaf layer'}, {'W m-2'}, ... - fnames.layer_Rn_file, n_col.layer_Rn, ns, true) + write_output({'thermal BlackBody emission spectrum in observation direction'}, {'W m-2 sr-1 um-1'}, ... + fnames.spectrum_obsdir_BlackBody_file, n_col.spectrum_obsdir_BlackBody, ns, true) + if options.calc_planck + write_output({'thermal emission spectrum in hemispherical direction'}, {'W m-2 sr-1 um-1'}, ... + fnames.spectrum_hemis_thermal_file, n_col.spectrum_hemis_thermal, ns, true) + write_output({'thermal emission spectrum in observation direction'}, {'W m-2 sr-1 um-1'}, ... + fnames.spectrum_obsdir_thermal_file, n_col.spectrum_obsdir_thermal, ns, true) + end end -if options.calc_fluor -fluorescence_names = {'supward fluorescence per layer'}; -fluorescence_units = {'W m-2'}; -write_output(fluorescence_names, fluorescence_units, fnames.layer_fluorescence_file, n_col.layer_fluorescence, ns, true) -end -end -if options.calc_fluor - write_output({'fluorescence per simulation for wavelengths of 640 to 850 nm, with 1 nm resolution'}, {'W m-2 um-1 sr-1'}, ... - fnames.fluorescence_file, n_col.fluorescence, ns, true) - if options.calc_PSI - write_output({'fluorescence per simulation for wavelengths of 640 to 850 nm, with 1 nm resolution, for PSI only'}, {'W m-2 um-1 sr-1'}, ... - fnames.fluorescencePSI_file, n_col.fluorescencePSI, ns, true) - write_output({'fluorescence per simulation for wavelengths of 640 to 850 nm, with 1 nm resolution, for PSII only'}, {'W m-2 um-1 sr-1'}, ... - fnames.fluorescencePSII_file, n_col.fluorescencePSII, ns, true) + write_output({'irradiance'}, {'W m-2 um-1'}, ... + fnames.irradiance_spectra_file, n_col.irradiance_spectra, ns, true) + write_output({'reflectance'}, {'fraction of radiation in observation direction *pi / irradiance'}, ... + fnames.reflectance_file, n_col.reflectance, ns, true) + %% input and parameter values (added June 2012) + %write_output(fnames.pars_and_input_file, true) + %write_output(fnames.pars_and_input_short_file, true) + %% Optional Output + if options.calc_vert_profiles + write_output({'Fraction leaves in the sun, fraction of observed, fraction of observed&visible per layer'}, {'rows: simulations or time steps, columns: layer numbers'}, ... + fnames.gap_file, n_col.gap, ns, true) + write_output({'aPAR per leaf layer'}, {'umol m-2 s-1'}, ... + fnames.layer_aPAR_file, n_col.layer_aPAR, ns, true) + write_output({'aPAR by Cab per leaf layer'}, {'umol m-2 s-1'}, ... + fnames.layer_aPAR_Cab_file, n_col.layer_aPAR_Cab, ns, true) + if options.calc_ebal + write_output({'leaf temperature of sunlit leaves, shaded leaves, and weighted average leaf temperature per layer'}, {'^oC ^oC ^oC'}, ... + fnames.leaftemp_file, n_col.leaftemp, ns, true) + write_output({'sensible heat flux per layer'}, {'W m-2'}, ... + fnames.layer_H_file, n_col.layer_H, ns, true) + write_output({'latent heat flux per layer'}, {'W m-2'}, ... + fnames.layer_LE_file, n_col.layer_LE, ns, true) + write_output({'photosynthesis per layer'}, {'umol m-2 s-1'}, ... + fnames.layer_A_file, n_col.layer_A, ns, true) + write_output({'average NPQ = 1-(fm-fo)/(fm0-fo0), per layer'}, {''}, ... + fnames.layer_NPQ_file, n_col.layer_NPQ, ns, true) + write_output({'net radiation per leaf layer'}, {'W m-2'}, ... + fnames.layer_Rn_file, n_col.layer_Rn, ns, true) + end + if options.calc_fluor + fluorescence_names = {'supward fluorescence per layer'}; + fluorescence_units = {'W m-2'}; + write_output(fluorescence_names, fluorescence_units, fnames.layer_fluorescence_file, n_col.layer_fluorescence, ns, true) end - write_output({'hemispherically integrated fluorescence per simulation for wavelengths of 640 to 850 nm, with 1 nm resolution'}, {'W m-2 um-1'}, ... - fnames.fluorescence_hemis_file, n_col.fluorescence_hemis, ns, true) - write_output({'total emitted fluorescence by all leaves for wavelengths of 640 to 850 nm, with 1 nm resolution'}, {'W m-2 um-1'}, ... - fnames.fluorescence_emitted_by_all_leaves_file, n_col.fluorescence_emitted_by_all_leaves, ns, true) - write_output({'total emitted fluorescence by all photosystems for wavelengths of 640 to 850 nm, with 1 nm resolution'}, {'W m-2 um-1'}, ... - fnames.fluorescence_emitted_by_all_photosystems_file, n_col.fluorescence_emitted_by_all_photosystems, ns, true) - write_output({'TOC fluorescence contribution from sunlit leaves for wavelengths of 640 to 850 nm, with 1 nm resolution'}, {'W m-2 um-1 sr-1'}, ... - fnames.fluorescence_sunlit_file, n_col.fluorescence_sunlit, ns, true) - write_output({'TOC fluorescence contribution from shaded leaves for wavelengths of 640 to 850 nm, with 1 nm resolution'}, {'W m-2 um-1 sr-1'}, ... - fnames.fluorescence_shaded_file, n_col.fluorescence_shaded, ns, true) - write_output({'TOC fluorescence contribution from from leaves and soil after scattering for wavelenghts of 640 to 850 nm, with 1 nm resolution'}, {'W m-2 um-1 sr-1'}, ... - fnames.fluorescence_scattered_file, n_col.fluorescence_scattered, ns, true) -end -write_output({'Bottom of canopy irradiance in the shaded fraction, and average BOC irradiance'}, {'First 2162 columns: shaded fraction. Last 2162 columns: average BOC irradiance. Unit: Wm-2 um-1'}, ... -fnames.BOC_irradiance_file, n_col.BOC_irradiance, ns, true) + end + if options.calc_fluor + write_output({'fluorescence per simulation for wavelengths of 640 to 850 nm, with 1 nm resolution'}, {'W m-2 um-1 sr-1'}, ... + fnames.fluorescence_file, n_col.fluorescence, ns, true) + if options.calc_PSI + write_output({'fluorescence per simulation for wavelengths of 640 to 850 nm, with 1 nm resolution, for PSI only'}, {'W m-2 um-1 sr-1'}, ... + fnames.fluorescencePSI_file, n_col.fluorescencePSI, ns, true) + write_output({'fluorescence per simulation for wavelengths of 640 to 850 nm, with 1 nm resolution, for PSII only'}, {'W m-2 um-1 sr-1'}, ... + fnames.fluorescencePSII_file, n_col.fluorescencePSII, ns, true) + end + write_output({'hemispherically integrated fluorescence per simulation for wavelengths of 640 to 850 nm, with 1 nm resolution'}, {'W m-2 um-1'}, ... + fnames.fluorescence_hemis_file, n_col.fluorescence_hemis, ns, true) + write_output({'total emitted fluorescence by all leaves for wavelengths of 640 to 850 nm, with 1 nm resolution'}, {'W m-2 um-1'}, ... + fnames.fluorescence_emitted_by_all_leaves_file, n_col.fluorescence_emitted_by_all_leaves, ns, true) + write_output({'total emitted fluorescence by all photosystems for wavelengths of 640 to 850 nm, with 1 nm resolution'}, {'W m-2 um-1'}, ... + fnames.fluorescence_emitted_by_all_photosystems_file, n_col.fluorescence_emitted_by_all_photosystems, ns, true) + write_output({'TOC fluorescence contribution from sunlit leaves for wavelengths of 640 to 850 nm, with 1 nm resolution'}, {'W m-2 um-1 sr-1'}, ... + fnames.fluorescence_sunlit_file, n_col.fluorescence_sunlit, ns, true) + write_output({'TOC fluorescence contribution from shaded leaves for wavelengths of 640 to 850 nm, with 1 nm resolution'}, {'W m-2 um-1 sr-1'}, ... + fnames.fluorescence_shaded_file, n_col.fluorescence_shaded, ns, true) + write_output({'TOC fluorescence contribution from from leaves and soil after scattering for wavelenghts of 640 to 850 nm, with 1 nm resolution'}, {'W m-2 um-1 sr-1'}, ... + fnames.fluorescence_scattered_file, n_col.fluorescence_scattered, ns, true) + end + write_output({'Bottom of canopy irradiance in the shaded fraction, and average BOC irradiance'}, {'First 2162 columns: shaded fraction. Last 2162 columns: average BOC irradiance. Unit: Wm-2 um-1'}, ... + fnames.BOC_irradiance_file, n_col.BOC_irradiance, ns, true) + + fclose('all'); -fclose('all'); + %% Delete all (temporary) .bin files + fn = fieldnames(fnames); + for k=1:numel(fn) + delete(fnames.(fn{k})) + end -%% deleting .bin -structfun(@delete, fnames) end function write_output(header, units, bin_path, f_n_col, ns, not_header) @@ -140,30 +144,35 @@ function write_output(header, units, bin_path, f_n_col, ns, not_header) not_header = false; end n_csv = strrep(bin_path, '.bin', '.csv'); - + f_csv = fopen(n_csv, 'w'); - %% in case header has more than one row - header_str = string(join(append(join(header, ','), '\n'), '')); - + header = cellstr(header); % cellstr is for MATLAB - Octave compatibility + headerSize = size(header); + nHeaderLines = headerSize(1); % If there is a multi-line header + headerString = repmat({}, nHeaderLines, 1); + for k=1:nHeaderLines + % cellstr is for MATLAB - Octave compatibility + headerString(k) = cellstr(strjoin(header(k, :), ",")); + end + headerString = strcat(strjoin(headerString, "\n"), "\n"); + if not_header - header_str = [header_str]; + headerString = [headerString]; else % it is a header => each column must have one assert(length(header) == f_n_col, 'Less headers than lines `%s` or n_col is wrong', bin_path) end - fprintf(f_csv, header_str); + + fprintf(f_csv, headerString); fprintf(f_csv, [strjoin(units, ','), '\n']); - + f_bin = fopen(bin_path, 'r'); out = fread(f_bin, 'double'); -% fclose(f_bin); % + some useconds to execution - + out_2d = reshape(out, f_n_col, ns)'; -% dlmwrite(n_csv, out_2d, '-append', 'precision', '%d'); % SLOW! for k=1:ns fprintf(f_csv, '%d,', out_2d(k, 1:end-1)); fprintf(f_csv, '%d\n', out_2d(k, end)); % saves from extra comma end -% fclose(f_csv); end diff --git a/src/+io/create_output_files_binary.m b/src/+io/create_output_files_binary.m index a7936824..5b72afeb 100644 --- a/src/+io/create_output_files_binary.m +++ b/src/+io/create_output_files_binary.m @@ -1,114 +1,108 @@ -function [Output_dir, f, fnames] = create_output_files_binary(parameter_file, sitename, path_of_code, input_path, output_path, spectral, options) -%% Create Output dir -string = clock; -simulation_name = char(sitename); -outdir_name = sprintf('%s_%4.0f-%02.0f-%02.0f-%02.0f%02.0f', simulation_name, string(1:5)); -Output_dir = [fullfile(output_path, outdir_name) filesep]; +function [Output_dir, fnames] = create_output_files_binary(parameter_file, sitename, path_of_code, input_path, output_path, spectral, options) + %% Set Output dir + Output_dir = output_path; -warning('off','MATLAB:DELETE:FileNotFound') -if any(~exist(Output_dir,'dir')) - mkdir(Output_dir) + %% Log File mkdir([Output_dir,'Parameters' filesep]) -end + for i = 1:length(parameter_file) + copy_name = [strrep(parameter_file{i}, '.csv', '') '_' sitename '.csv']; + copyfile([input_path parameter_file{i}],[Output_dir,'Parameters/', copy_name],'f') + end + fidpath = fopen([Output_dir,'Parameters/SCOPEversion.txt'],'w'); % complete path of the SCOPE code + fprintf(fidpath,'%s', path_of_code); -%% Log File -for i = 1:length(parameter_file) - copy_name = [strrep(parameter_file{i}, '.csv', '') '_' outdir_name '.csv']; - copyfile([input_path parameter_file{i}],[Output_dir,'Parameters/', copy_name],'f') -end -fidpath = fopen([Output_dir,'Parameters/SCOPEversion.txt'],'w'); % complete path of the SCOPE code -fprintf(fidpath,'%s', path_of_code); + %% Filenames, will become .csv if options is on + fnames.flu_file = fullfile(Output_dir,'fluxes.bin'); + fnames.surftemp_file = fullfile(Output_dir,'surftemp.bin'); + fnames.aerodyn_file = fullfile(Output_dir,'aerodyn.bin'); + fnames.radiation_file = fullfile(Output_dir,'radiation.bin'); + fnames.fluorescence_file = fullfile(Output_dir,'fluorescence.bin'); + fnames.wl_file = fullfile(Output_dir,'wl.bin'); % wavelength + fnames.irradiance_spectra_file = fullfile(Output_dir,'irradiance_spectra.bin'); % Fluorescence + fnames.spectrum_hemis_optical_file = fullfile(Output_dir,'spectrum_hemis.bin'); + fnames.spectrum_obsdir_optical_file = fullfile(Output_dir,'spectrum_obsdir.bin'); + fnames.reflectance_file = fullfile(Output_dir,'reflectance.bin'); % reflectance spectrum + fnames.BOC_irradiance_file = fullfile(Output_dir,'BOC_irradiance.bin'); % reflectance spectrum + fnames.Sim_Theta_file = fullfile(Output_dir,'Sim_Theta.bin'); % soil moisture + fnames.Sim_Temp_file = fullfile(Output_dir,'Sim_Temp.bin'); % soil temperature + if options.calc_ebal + fnames.spectrum_obsdir_BlackBody_file = fullfile(Output_dir,'spectrum_obsdir_BlackBody.bin'); % spectrum observation direction + end -%% Filenames, will become .csv if options is on -fnames.flu_file = fullfile(Output_dir,'fluxes.bin'); -fnames.surftemp_file = fullfile(Output_dir,'surftemp.bin'); -fnames.aerodyn_file = fullfile(Output_dir,'aerodyn.bin'); -fnames.radiation_file = fullfile(Output_dir,'radiation.bin'); -fnames.fluorescence_file = fullfile(Output_dir,'fluorescence.bin'); -fnames.wl_file = fullfile(Output_dir,'wl.bin'); % wavelength -fnames.irradiance_spectra_file = fullfile(Output_dir,'irradiance_spectra.bin'); % Fluorescence -fnames.spectrum_hemis_optical_file = fullfile(Output_dir,'spectrum_hemis.bin'); -fnames.spectrum_obsdir_optical_file = fullfile(Output_dir,'spectrum_obsdir.bin'); -fnames.reflectance_file = fullfile(Output_dir,'reflectance.bin'); % reflectance spectrum -fnames.BOC_irradiance_file = fullfile(Output_dir,'BOC_irradiance.bin'); % reflectance spectrum -fnames.Sim_Theta_file = fullfile(Output_dir,'Sim_Theta.bin'); % soil moisture -fnames.Sim_Temp_file = fullfile(Output_dir,'Sim_Temp.bin'); % soil temperature -if options.calc_ebal - fnames.spectrum_obsdir_BlackBody_file = fullfile(Output_dir,'spectrum_obsdir_BlackBody.bin'); % spectrum observation direction -end + %if ~(options.simulation==1) + fnames.pars_and_input_file = fullfile(Output_dir,'pars_and_input.bin'); % wavelength + + %for j = 1:length(V) + % fprintf(fidv,'%s\t',V(j).Name); + %end + %fprintf(fidv,'\r'); + %end -%if ~(options.simulation==1) -fnames.pars_and_input_file = fullfile(Output_dir,'pars_and_input.bin'); % wavelength + %if ~(options.simulation==1) + fnames.pars_and_input_short_file = fullfile(Output_dir,'pars_and_input_short.bin'); % wavelength + %for j = find(vmax>1) + % fprintf(fidvs,'%s\t',V(vmax>1).Name); + %end + %fprintf(fidvs,' \r'); + % + %% Optional Output + if options.calc_vert_profiles + fnames.gap_file = fullfile(Output_dir,'gap.bin'); % gap + fnames.leaftemp_file = fullfile(Output_dir,'leaftemp.bin'); % leaftemp + fnames.layer_H_file = fullfile(Output_dir,'layer_H.bin'); % vertical profile + fnames.layer_LE_file = fullfile(Output_dir,'layer_lE.bin'); % latent heat + fnames.layer_A_file = fullfile(Output_dir,'layer_A.bin'); % + fnames.layer_aPAR_file = fullfile(Output_dir,'layer_aPAR.bin'); % + fnames.layer_aPAR_Cab_file = fullfile(Output_dir,'layer_aPAR_Cab.bin'); % + fnames.layer_Rn_file = fullfile(Output_dir,'layer_Rn.bin'); % + if options.calc_fluor + fnames.layer_fluorescence_file = fullfile(Output_dir,'layer_fluorescence.bin'); % + fnames.layer_fluorescenceEm_file = fullfile(Output_dir,'layer_fluorescenceEm.bin'); % + fnames.layer_NPQ_file = fullfile(Output_dir,'layer_NPQ.bin'); % + end -%for j = 1:length(V) - % fprintf(fidv,'%s\t',V(j).Name); -%end -%fprintf(fidv,'\r'); -%end + else + delete([Output_dir,'leaftemp.bin']) + delete([Output_dir,'layer_H.bin']) + delete([Output_dir,'layer_lE.bin']) + delete([Output_dir,'layer_A.bin']) + delete([Output_dir,'layer_aPAR.bin']) + delete([Output_dir,'layer_Rn.bin']) + end -%if ~(options.simulation==1) -fnames.pars_and_input_short_file = fullfile(Output_dir,'pars_and_input_short.bin'); % wavelength -%for j = find(vmax>1) - % fprintf(fidvs,'%s\t',V(vmax>1).Name); -%end -%fprintf(fidvs,' \r'); -% -%% Optional Output -if options.calc_vert_profiles - fnames.gap_file = fullfile(Output_dir,'gap.bin'); % gap - fnames.leaftemp_file = fullfile(Output_dir,'leaftemp.bin'); % leaftemp - fnames.layer_H_file = fullfile(Output_dir,'layer_H.bin'); % vertical profile - fnames.layer_LE_file = fullfile(Output_dir,'layer_lE.bin'); % latent heat - fnames.layer_A_file = fullfile(Output_dir,'layer_A.bin'); % - fnames.layer_aPAR_file = fullfile(Output_dir,'layer_aPAR.bin'); % - fnames.layer_aPAR_Cab_file = fullfile(Output_dir,'layer_aPAR_Cab.bin'); % - fnames.layer_Rn_file = fullfile(Output_dir,'layer_Rn.bin'); % if options.calc_fluor - fnames.layer_fluorescence_file = fullfile(Output_dir,'layer_fluorescence.bin'); % - fnames.layer_fluorescenceEm_file = fullfile(Output_dir,'layer_fluorescenceEm.bin'); % - fnames.layer_NPQ_file = fullfile(Output_dir,'layer_NPQ.bin'); % + fnames.fluorescence_file = fullfile(Output_dir,'fluorescence.bin'); % Fluorescence + if options.calc_PSI + fnames.fluorescencePSI_file = fullfile(Output_dir,'fluorescencePSI.bin'); % Fluorescence + fnames.fluorescencePSII_file = fullfile(Output_dir,'fluorescencePSII.bin'); % Fluorescence + end + fnames.fluorescence_hemis_file = fullfile(Output_dir,'fluorescence_hemis.bin'); % Fluorescence + fnames.fluorescence_emitted_by_all_leaves_file = fullfile(Output_dir,'fluorescence_emitted_by_all_leaves.bin'); + fnames.fluorescence_emitted_by_all_photosystems_file = fullfile(Output_dir,'fluorescence_emitted_by_all_photosystems.bin'); + fnames.fluorescence_sunlit_file = fullfile(Output_dir,'fluorescence_sunlit.bin'); % Fluorescence + fnames.fluorescence_shaded_file = fullfile(Output_dir,'fluorescence_shaded.bin'); % Fluorescence + fnames.fluorescence_scattered_file = fullfile(Output_dir,'fluorescence_scattered.bin'); % Fluorescence + else + delete([Output_dir,'fluorescence.bin']) end - -else - delete([Output_dir,'leaftemp.bin']) - delete([Output_dir,'layer_H.bin']) - delete([Output_dir,'layer_lE.bin']) - delete([Output_dir,'layer_A.bin']) - delete([Output_dir,'layer_aPAR.bin']) - delete([Output_dir,'layer_Rn.bin']) -end -if options.calc_fluor - fnames.fluorescence_file = fullfile(Output_dir,'fluorescence.bin'); % Fluorescence - if options.calc_PSI - fnames.fluorescencePSI_file = fullfile(Output_dir,'fluorescencePSI.bin'); % Fluorescence - fnames.fluorescencePSII_file = fullfile(Output_dir,'fluorescencePSII.bin'); % Fluorescence + if options.calc_directional + delete([Output_dir,'BRDF/*.bin']) end - fnames.fluorescence_hemis_file = fullfile(Output_dir,'fluorescence_hemis.bin'); % Fluorescence - fnames.fluorescence_emitted_by_all_leaves_file = fullfile(Output_dir,'fluorescence_emitted_by_all_leaves.bin'); - fnames.fluorescence_emitted_by_all_photosystems_file = fullfile(Output_dir,'fluorescence_emitted_by_all_photosystems.bin'); - fnames.fluorescence_sunlit_file = fullfile(Output_dir,'fluorescence_sunlit.bin'); % Fluorescence - fnames.fluorescence_shaded_file = fullfile(Output_dir,'fluorescence_shaded.bin'); % Fluorescence - fnames.fluorescence_scattered_file = fullfile(Output_dir,'fluorescence_scattered.bin'); % Fluorescence -else - delete([Output_dir,'fluorescence.bin']) -end -if options.calc_directional - delete([Output_dir,'BRDF/*.bin']) -end + if options.calc_planck && options.calc_ebal + fnames.spectrum_obsdir_thermal_file = fullfile(Output_dir,'spectrum_obsdir_thermal.bin'); % spectrum observation direction + fnames.spectrum_hemis_thermal_file = fullfile(Output_dir,'spectrum_hemis_thermal.bin'); % spectrum hemispherically integrated + end -if options.calc_planck && options.calc_ebal - fnames.spectrum_obsdir_thermal_file = fullfile(Output_dir,'spectrum_obsdir_thermal.bin'); % spectrum observation direction - fnames.spectrum_hemis_thermal_file = fullfile(Output_dir,'spectrum_hemis_thermal.bin'); % spectrum hemispherically integrated -end -%% Open files for writing -f = structfun(@(x) fopen(x, 'w'), fnames, 'UniformOutput',false); + % Create empty files for appending + structfun(@(x) fopen(x, 'w'), fnames, 'UniformOutput',false); + fclose("all"); -%% write wl -wlS = spectral.wlS; %#ok<*NASGU> -wlF = spectral.wlF; + %% write wl + wlS = spectral.wlS; %#ok<*NASGU> + wlF = spectral.wlF; -save([Output_dir 'wlS.txt'], 'wlS', '-ascii'); -save([Output_dir 'wlF.txt'], 'wlF', '-ascii'); + save([Output_dir 'wlS.txt'], 'wlS', '-ascii'); + save([Output_dir 'wlF.txt'], 'wlF', '-ascii'); end diff --git a/src/+io/output_data_binary.m b/src/+io/output_data_binary.m index 16dd97e0..0039b020 100644 --- a/src/+io/output_data_binary.m +++ b/src/+io/output_data_binary.m @@ -1,15 +1,7 @@ function n_col = output_data_binary(f, k, xyt, rad, canopy, V, vi, vmax, options, fluxes, meteo, iter, thermal,spectral, gap, profiles, Sim_Theta_U, Sim_Temp, Trap, Evap) %% OUTPUT DATA % author C. Van der Tol -% date: 30 Nov 2019 - -%% -if isdatetime(xyt.t) - get_doy = @(x) juliandate(x) - juliandate(datetime(year(x), 1, 0)); - V(46).Val = get_doy(timestamp2datetime(xyt.startDOY)); - V(47).Val = get_doy(timestamp2datetime(xyt.endDOY)); - xyt.t = get_doy(xyt.t); -end +% date: 30 Nov 2019 %% Fluxes product flu_out = [k iter.counter xyt.year(k) xyt.t(k) fluxes.Rntot fluxes.lEtot fluxes.Htot fluxes.Rnctot fluxes.lEctot, ... @@ -54,24 +46,24 @@ spectrum_obsdir_BlackBody_out = [rad.LotBB_']; n_col.spectrum_obsdir_BlackBody = length(spectrum_obsdir_BlackBody_out); fwrite(f.spectrum_obsdir_BlackBody_file,spectrum_obsdir_BlackBody_out,'double'); - + if options.calc_planck - - spectrum_hemis_thermal_out = [rad.Eoutte_']; + + spectrum_hemis_thermal_out = [rad.Eoutte_']; n_col.spectrum_hemis_thermal = length(spectrum_hemis_thermal_out); fwrite(f.spectrum_hemis_thermal_file,spectrum_hemis_thermal_out,'double'); - + spectrum_obsdir_thermal_out = [rad.Lot_']; n_col.spectrum_obsdir_thermal = length(spectrum_obsdir_thermal_out); fwrite(f.spectrum_obsdir_thermal_file,spectrum_obsdir_thermal_out,'double'); - + end end irradiance_spectra_out = [meteo.Rin*(rad.fEsuno+rad.fEskyo)']; n_col.irradiance_spectra = length(irradiance_spectra_out); fwrite(f.irradiance_spectra_file,irradiance_spectra_out,'double'); - + reflectance = pi*rad.Lo_./(rad.Esun_+rad.Esky_); reflectance(spectral.wlS>3000) = NaN; reflectance_out = [reflectance']; @@ -93,52 +85,52 @@ %% Optional Output if options.calc_vert_profiles - + % gap gap_out = [gap.Ps gap.Po gap.Pso]; n_col.gap = numel(gap_out(:)); fwrite(f.gap_file,gap_out,'double'); - + layer_aPAR_out = [1E6*profiles.Pn1d' 0]; n_col.layer_aPAR = length(layer_aPAR_out); fwrite(f.layer_aPAR_file,layer_aPAR_out,'double'); - + layer_aPAR_Cab_out = [1E6*profiles.Pn1d_Cab' 0]; n_col.layer_aPAR_Cab = length(layer_aPAR_Cab_out); - fwrite(f.layer_aPAR_Cab_file,layer_aPAR_Cab_out,'double'); - + fwrite(f.layer_aPAR_Cab_file,layer_aPAR_Cab_out,'double'); + if options.calc_ebal - - % leaftemp + + % leaftemp leaftemp_out = [profiles.Tcu1d' profiles.Tch' profiles.Tc1d']; n_col.leaftemp = length(leaftemp_out); - fwrite(f.leaftemp_file,leaftemp_out,'double'); - + fwrite(f.leaftemp_file,leaftemp_out,'double'); + layer_H_out = [profiles.Hc1d' fluxes.Hstot]; n_col.layer_H = length(layer_H_out); - fwrite(f.layer_H_file,layer_H_out,'double'); - + fwrite(f.layer_H_file,layer_H_out,'double'); + layer_LE_out = [profiles.lEc1d' fluxes.lEstot]; n_col.layer_LE = length(layer_LE_out); - fwrite(f.layer_LE_file,layer_LE_out,'double'); - + fwrite(f.layer_LE_file,layer_LE_out,'double'); + layer_A_out = [profiles.A1d' fluxes.Resp]; n_col.layer_A = length(layer_A_out); - fwrite(f.layer_A_file,layer_A_out,'double'); - + fwrite(f.layer_A_file,layer_A_out,'double'); + layer_NPQ_out = [profiles.qE' 0]; n_col.layer_NPQ = length(layer_NPQ_out); - fwrite(f.layer_NPQ_file,layer_NPQ_out,'double'); - + fwrite(f.layer_NPQ_file,layer_NPQ_out,'double'); + layer_Rn_out = [profiles.Rn1d' fluxes.Rnstot]; n_col.layer_Rn = length(layer_Rn_out); - fwrite(f.layer_Rn_file,layer_Rn_out,'double'); - + fwrite(f.layer_Rn_file,layer_Rn_out,'double'); + end if options.calc_fluor layer_fluorescence_out = [profiles.fluorescence']; n_col.layer_fluorescence = length(layer_fluorescence_out); - fwrite(f.layer_fluorescence_file,layer_fluorescence_out,'double'); + fwrite(f.layer_fluorescence_file,layer_fluorescence_out,'double'); end end @@ -146,47 +138,47 @@ for j=1:size(spectral.wlF,1) fluorescence_out = [rad.LoF_]; n_col.fluorescence = length(fluorescence_out); - fwrite(f.fluorescence_file,fluorescence_out,'double'); - + fwrite(f.fluorescence_file,fluorescence_out,'double'); + if options.calc_PSI fluorescencePSI_out = [rad.LoF1_]; n_col.fluorescencePSI = length(fluorescencePSI_out); - fwrite(f.fluorescencePSI_file,fluorescencePSI_out,'double'); - + fwrite(f.fluorescencePSI_file,fluorescencePSI_out,'double'); + fluorescencePSII_out = [rad.LoF2_]; n_col.fluorescencePSII = length(fluorescencePSII_out); - fwrite(f.fluorescencePSII_file,fluorescencePSII_out,'double'); + fwrite(f.fluorescencePSII_file,fluorescencePSII_out,'double'); end - + fluorescence_hemis_out = [rad.Fhem_]; n_col.fluorescence_hemis = length(fluorescence_hemis_out); - fwrite(f.fluorescence_hemis_file,fluorescence_hemis_out,'double'); - + fwrite(f.fluorescence_hemis_file,fluorescence_hemis_out,'double'); + fluorescence_emitted_by_all_leaves_out = [rad.Fem_]; n_col.fluorescence_emitted_by_all_leaves = length(fluorescence_emitted_by_all_leaves_out); - fwrite(f.fluorescence_emitted_by_all_leaves_file,fluorescence_emitted_by_all_leaves_out,'double'); - + fwrite(f.fluorescence_emitted_by_all_leaves_file,fluorescence_emitted_by_all_leaves_out,'double'); + fluorescence_emitted_by_all_photosystems_out = [rad.Femtot]; n_col.fluorescence_emitted_by_all_photosystems = length(fluorescence_emitted_by_all_photosystems_out); - fwrite(f.fluorescence_emitted_by_all_photosystems_file,fluorescence_emitted_by_all_photosystems_out,'double'); - + fwrite(f.fluorescence_emitted_by_all_photosystems_file,fluorescence_emitted_by_all_photosystems_out,'double'); + fluorescence_sunlit_out = [sum(rad.LoF_sunlit,2)]; n_col.fluorescence_sunlit = length(fluorescence_sunlit_out); - fwrite(f.fluorescence_sunlit_file,fluorescence_sunlit_out,'double'); - + fwrite(f.fluorescence_sunlit_file,fluorescence_sunlit_out,'double'); + fluorescence_shaded_out = [sum(rad.LoF_shaded,2)]; n_col.fluorescence_shaded = length(fluorescence_shaded_out); - fwrite(f.fluorescence_shaded_file,fluorescence_shaded_out,'double'); - + fwrite(f.fluorescence_shaded_file,fluorescence_shaded_out,'double'); + fluorescence_scattered_out = [sum(rad.LoF_scattered,2)+sum(rad.LoF_soil,2)]; n_col.fluorescence_scattered = length(fluorescence_scattered_out); - fwrite(f.fluorescence_scattered_file,fluorescence_scattered_out,'double'); - - end + fwrite(f.fluorescence_scattered_file,fluorescence_scattered_out,'double'); + + end end BOC_irradiance_out = [rad.Emin_(canopy.nlayers+1,:),rad.Emin_(canopy.nlayers+1,:)+(rad.Esun_*gap.Ps(canopy.nlayers+1)')']; n_col.BOC_irradiance = length(BOC_irradiance_out); - fwrite(f.BOC_irradiance_file,BOC_irradiance_out,'double'); + fwrite(f.BOC_irradiance_file,BOC_irradiance_out,'double'); %% if options.calc_directional && options.calc_ebal @@ -200,15 +192,15 @@ if options.calc_fluor Output_fluor = [spectral.wlF' directional.LoF_]; end - + save([Output_dir,'Directional/',sprintf('BRDF (SunAngle %2.2f degrees).dat',angles.tts)],'Output_brdf' ,'-ASCII','-TABS') save([Output_dir,'Directional/',sprintf('Angles (SunAngle %2.2f degrees).dat',angles.tts)],'Output_angle','-ASCII','-TABS') save([Output_dir,'Directional/',sprintf('Temperatures (SunAngle %2.2f degrees).dat',angles.tts)],'Output_temp','-ASCII','-TABS') - + if options.calc_fluor save([Output_dir,'Directional/',sprintf('Fluorescence (SunAngle %2.2f degrees).dat',angles.tts)],'Output_fluor','-ASCII','-TABS') end - + fiddirtir = fopen([Output_dir,'Directional/','read me.txt'],'w'); fprintf(fiddirtir,'The Directional data is written in three files: \r\n'); fprintf(fiddirtir,'\r\n- Angles: contains the directions. \r\n'); diff --git a/src/+io/prepareForcingData.m b/src/+io/prepareForcingData.m deleted file mode 100644 index e0f3c4a6..00000000 --- a/src/+io/prepareForcingData.m +++ /dev/null @@ -1,143 +0,0 @@ -function [SiteProperties, timeStep, forcingTimeLength] = prepareForcingData(DataPaths, forcingFileName) -%{ -This function is used to read forcing data and site properties. - -Input: - DataPaths: A construct contains paths of data. - forcingFileName: A string of the name of forcing NetCDF data. - -Output: - SiteProperties: A structure containing site properties variables. - timeStep: Time interval in seconds, normal is 1800 s in STEMMUS-SCOPE. - timeLength: The total number of time steps in forcing data. -%} -%% -% Add the forcing name to forcing path -ForcingFilePath=fullfile(DataPaths.forcingPath, forcingFileName); -% Prepare input files -sitefullname=dir(ForcingFilePath).name; %read sitename -SiteProperties.siteName=sitefullname(1:6); -startyear=sitefullname(8:11); -endyear=sitefullname(13:16); -startyear=str2double(startyear); -endyear=str2double(endyear); - -% Read time values from forcing file -time1=ncread(ForcingFilePath,'time'); -t1=datenum(startyear,1,1,0,0,0); -timeStep=time1(2); - -%get time length of forcing file -forcingTimeLength=length(time1); - -dt=time1(2)/3600/24; -t2=datenum(endyear,12,31,23,30,0); -T=t1:dt:t2; -TL=length(T); -T=T'; -T=datestr(T,'yyyy-mm-dd HH:MM:SS'); -T0=T(:,1:4); -T1=T(:,5:19); -T3=T1(1,:); -T4=T3(ones(TL,1),:); -T5=[T0,T4]; -T6=datenum(T); -T7=datenum(T5); -T8=T6-T7; -time=T8; -T10=year(T); - -RH=ncread(ForcingFilePath,'RH'); % Unit: % -RHL=length(RH); -RHa=reshape(RH,RHL,1); - -Tair=ncread(ForcingFilePath,'Tair'); % Unit: K -TairL=length(Tair); -Taira=reshape(Tair,TairL,1)-273.15; % Unit: degree C - -es= 6.107*10.^(Taira.*7.5./(237.3+Taira)); % Unit: hPa -ea=es.*RHa./100; - -SWdown=ncread(ForcingFilePath,'SWdown'); % Unit: W/m2 -SWdownL=length(SWdown); -SWdowna=reshape(SWdown,SWdownL,1); - - -LWdown=ncread(ForcingFilePath,'LWdown'); % Unit: W/m2 -LWdownL=length(LWdown); -LWdowna=reshape(LWdown,LWdownL,1); - - -VPD=ncread(ForcingFilePath,'VPD'); % Unit: hPa -VPDL=length(VPD); -VPDa=reshape(VPD,VPDL,1); - - -% Qair=ncread(ForcingFilePath,'Qair'); -% QairL=length(Qair); -% Qaira=reshape(Qair,QairL,1); - - -Psurf=ncread(ForcingFilePath,'Psurf'); % Unit: Pa -PsurfL=length(Psurf); -Psurfa=reshape(Psurf,PsurfL,1); -Psurfa=Psurfa./100; % Unit: hPa - - -Precip=ncread(ForcingFilePath,'Precip'); % Unit: mm/s -PrecipL=length(Precip); -Precipa=reshape(Precip,PrecipL,1); -Precipa=Precipa./10; % Unit: cm/s - - -Wind=ncread(ForcingFilePath,'Wind'); % Unit: m/s -WindL=length(Wind); -Winda=reshape(Wind,WindL,1); - - -CO2air=ncread(ForcingFilePath,'CO2air'); % Unit: ppm -CO2airL=length(CO2air); -CO2aira=reshape(CO2air,CO2airL,1); -CO2aira=CO2aira.*44./22.4; % Unit: mg/m3 - - -SiteProperties.latitude=ncread(ForcingFilePath,'latitude'); -SiteProperties.longitude=ncread(ForcingFilePath,'longitude'); -SiteProperties.elevation=ncread(ForcingFilePath,'elevation'); - -LAI=ncread(ForcingFilePath,'LAI'); -LAIL=length(LAI); -LAIa=reshape(LAI,LAIL,1); -LAIa(LAIa<0.01)=0.01; - -% LAI_alternative=ncread(ForcingFilePath,'LAI_alternative'); -% LAI_alternativeL=length(LAI_alternative); -% LAI_alternativea=reshape(LAI_alternative,LAI_alternativeL,1); - -SiteProperties.igbpVegLong=ncread(ForcingFilePath,'IGBP_veg_long'); -SiteProperties.referenceHeight=ncread(ForcingFilePath,'reference_height'); -SiteProperties.canopyHeight=ncread(ForcingFilePath,'canopy_height'); - -save([DataPaths.input, 't_.dat'], '-ascii', 'time') -save([DataPaths.input, 'Ta_.dat'], '-ascii', 'Taira') -save([DataPaths.input, 'Rin_.dat'], '-ascii', 'SWdowna') -save([DataPaths.input, 'Rli_.dat'], '-ascii', 'LWdowna') -%save([DataPaths.input, 'VPDa.dat'], '-ascii', 'VPDa') -%save([DataPaths.input, 'Qaira.dat'], '-ascii', 'Qaira') -save([DataPaths.input, 'p_.dat'], '-ascii', 'Psurfa') -%save([DataPaths.input, 'Precipa.dat'], '-ascii', 'Precipa') -save([DataPaths.input, 'u_.dat'], '-ascii', 'Winda') -%save([DataPaths.input, 'RHa.dat'], '-ascii', 'RHa') -save([DataPaths.input, 'CO2_.dat'], '-ascii', 'CO2aira') -%save([DataPaths.input, 'latitude.dat'], '-ascii', 'latitude') -%save([DataPaths.input, 'longitude.dat'], '-ascii', 'longitude') -%save([DataPaths.input, 'reference_height.dat'], '-ascii', 'reference_height') -%save([DataPaths.input, 'canopy_height.dat'], '-ascii', 'canopy_height') -%save([DataPaths.input, 'elevation.dat'], '-ascii', 'elevation') -save([DataPaths.input, 'ea_.dat'], '-ascii', 'ea') -save([DataPaths.input, 'year_.dat'], '-ascii', 'T10') -LAI=[time'; LAIa']'; -save([DataPaths.input, 'LAI_.dat'], '-ascii', 'LAI') %save meteorological data for STEMMUS -Meteodata=[time';Taira';RHa';Winda';Psurfa';Precipa';SWdowna';LWdowna';VPDa';LAIa']'; -save([DataPaths.input, 'Mdata.txt'], '-ascii', 'Meteodata') %save meteorological data for STEMMUS -end \ No newline at end of file diff --git a/src/+io/prepareInputData.m b/src/+io/prepareInputData.m new file mode 100644 index 00000000..11b2bec4 --- /dev/null +++ b/src/+io/prepareInputData.m @@ -0,0 +1,34 @@ +function [SiteProperties, SoilProperties, TimeProperties] = prepareInputData(InputPath) + %{ + This function is used to read forcing data and site properties. + + Input: + InputPath: path of input data. + + Output: + SiteProperties: A structure containing site properties variables. + SoilProperties: A structure containing soil variables. + TimeProperties: A structure containing time variables like time interval in seconds, normal is 1800 s in STEMMUS-SCOPE + and the total number of time steps. + %} + + % The "forcing_globals.mat" and "soil_parameters.mat" files are generated using "PyStemmusScope" + % python package, see https://github.com/EcoExtreML/STEMMUS_SCOPE_Processing + forcing_global_path = fullfile(InputPath, 'forcing_globals.mat'); + soil_global_path = fullfile(InputPath, 'soil_parameters.mat'); + + %% Explicitly load all variables into the workspace + TimeProperties = load(forcing_global_path, 'DELT', 'Dur_tot'); + SiteProperties = load(forcing_global_path, 'IGBP_veg_long', 'latitude', 'longitude', 'reference_height', 'canopy_height', 'sitename'); + + SoilProperties = load(soil_global_path, 'SaturatedK', 'SaturatedMC', 'ResidualMC', 'Coefficient_n', 'Coefficient_Alpha', ... + 'porosity', 'FOC', 'FOS', 'MSOC', 'Coef_Lamda', 'fieldMC', 'fmax', 'theta_s0', 'Ks0'); + + % Convert the int vectors back to strings + SiteProperties.sitename = char(SiteProperties.sitename); + SiteProperties.IGBP_veg_long = char(SiteProperties.IGBP_veg_long); + + % The model expects the char vector to be transposed: + SiteProperties.IGBP_veg_long = transpose(SiteProperties.IGBP_veg_long); + +end \ No newline at end of file diff --git a/src/+io/readStructFromExcel.m b/src/+io/readStructFromExcel.m index 30e0ff2a..210ac7a8 100644 --- a/src/+io/readStructFromExcel.m +++ b/src/+io/readStructFromExcel.m @@ -1,52 +1,53 @@ function data = readStructFromExcel(filename, sheetName, headerIdx, dataIdx, data_is_char, data_in_rows) -% Read data into a struct with names matching those found in the first column/row -% default is for data to be in columns (A and B); if data_in_rows = true, data are in rows 1 & 2 -% example: -% readStructFromExcel('../input_data.xlsx', 'options', 3, 1) -% readStructFromExcel('../input_data.xlsx', 'filenames', 1, 2, true) + % Read data into a struct with names matching those found in the first column/row + % default is for data to be in columns (A and B); if data_in_rows = true, data are in rows 1 & 2 + % example: + % readStructFromExcel('../input_data.xlsx', 'options', 3, 1) + % readStructFromExcel('../input_data.xlsx', 'filenames', 1, 2, true) -% default values: -if nargin < 3 - headerIdx = 1; -end -if nargin < 4 - dataIdx = 2; -end -if nargin < 5 - data_is_char = false; -end -if nargin < 6 - data_in_rows = false; -end + % default values: + if nargin < 3 + headerIdx = 1; + end + if nargin < 4 + dataIdx = 2; + end + if nargin < 5 + data_is_char = false; + end + if nargin < 6 + data_in_rows = false; + end -% read in the spreadsheet -% general note: work with all_as_cell to keep rows & columns in sync; MATLAB does NOT keep data and texts aligned -% (readtable only slightly better - it works but will convert mixed columns to string) -if data_in_rows - %NOTE: 'basic' is compatible with Mac but MATLAB (2013b) complains it can't read Unicode '.xls' in basic mode - % Solution: save as xlsx ?! - [~, ~, allCells] = xlsread(filename, sheetName, ''); % = [data, texts, allCells] -else - [~, ~, allCells] = xlsread(filename, sheetName, ''); % = [data, texts, allCells] - allCells = allCells'; % transpose so data are in the same column as headers -end -% data are now in columns + % read in the spreadsheet + % general note: work with all_as_cell to keep rows & columns in sync; MATLAB does NOT keep data and texts aligned + % (readtable only slightly better - it works but will convert mixed columns to string) + if data_in_rows + %NOTE: 'basic' is compatible with Mac but MATLAB (2013b) complains it can't read Unicode '.xls' in basic mode + % Solution: save as xlsx ?! + [~, ~, allCells] = xlsread(filename, sheetName); % = [data, texts, allCells] + else + [~, ~, allCells] = xlsread(filename, sheetName); % = [data, texts, allCells] + allCells = allCells'; % transpose so data are in the same column as headers + end + % data are now in columns -% delete empty columns - % define two "helper functions" for eliminating null entries -isNotNumeric = @(x) cellfun(@(y) ischar(y) | any(isnan(y)), x); % any is needed because matlab treats string as char array -isCharCell = @(x) cellfun(@(y) ischar(y), x); + % delete empty columns + % define two "helper functions" for eliminating null entries + isNotNumeric = @(x) cellfun(@(y) ischar(y) | any(isnan(y)), x); % any is needed because matlab treats string as char array + isCharCell = @(x) cellfun(@(y) ischar(y), x); -validHeaders = arrayfun(isCharCell, allCells(headerIdx, :)); -if data_is_char - validData = arrayfun(isCharCell, allCells(dataIdx, :)); % , 'UniformOutput', true -else % numeric data: - validData = ~arrayfun(isNotNumeric, allCells(dataIdx, :)); % , 'UniformOutput', true -end + validHeaders = arrayfun(isCharCell, allCells(headerIdx, :)); + if data_is_char + validData = arrayfun(isCharCell, allCells(dataIdx, :)); % , 'UniformOutput', true + else % numeric data: + validData = ~arrayfun(isNotNumeric, allCells(dataIdx, :)); % , 'UniformOutput', true + end -dataCells = allCells([headerIdx, dataIdx], validHeaders & validData); + dataCells = allCells([headerIdx, dataIdx], validHeaders & validData); -for idx = 1:size(dataCells,2) - varName = strrep(dataCells{1, idx}, ' ', ''); - data.(varName) = dataCells{2, idx}; + for idx = 1:size(dataCells,2) + varName = strrep(dataCells{1, idx}, ' ', ''); + data.(varName) = dataCells{2, idx}; + end end \ No newline at end of file diff --git a/src/+io/read_config.m b/src/+io/read_config.m index a8f3840c..acf1eef5 100644 --- a/src/+io/read_config.m +++ b/src/+io/read_config.m @@ -1,4 +1,4 @@ -function [SoilPropertyPath, InputPath, OutputPath, ForcingPath, ForcingFileName, DurationSize, InitialConditionPath] = read_config(config_file) +function [InputPath, OutputPath, InitialConditionPath] = read_config(config_file) file_id = fopen(config_file); config = textscan(file_id,'%s %s', 'HeaderLines',0, 'Delimiter', '='); @@ -9,24 +9,11 @@ config_paths = config{2}; %% find the required path by model -indx = find(strcmp(config_vars, 'SoilPropertyPath')); -SoilPropertyPath = config_paths{indx}; - indx = find(strcmp(config_vars, 'InputPath')); InputPath = config_paths{indx}; indx = find(strcmp(config_vars, 'OutputPath')); OutputPath = config_paths{indx}; -indx = find(strcmp(config_vars, 'ForcingPath')); -ForcingPath = config_paths{indx}; - -indx = find(strcmp(config_vars, 'ForcingFileName')); -ForcingFileName = config_paths{indx}; - indx = find(strcmp(config_vars, 'InitialConditionPath')); InitialConditionPath = config_paths{indx}; - -% value of DurationSize is optional and can be NA -indx = find(strcmp(config_vars, 'DurationSize')); -DurationSize = str2double(config_paths{indx}); diff --git a/src/Constants.m b/src/Constants.m index d14fd0f2..194d1503 100644 --- a/src/Constants.m +++ b/src/Constants.m @@ -1,73 +1,73 @@ % function Constants global DeltZ Delt_t ML NS mL mN nD NL NN SAVE Tot_Depth Tmin global xERR hERR TERR PERR tS uERR -global KT TIME Delt_t0 DURTN TEND NIT KIT Nmsrmn Eqlspace h_SUR Msrmn_Fitting +global KT TIME Delt_t0 DURTN TEND NIT KIT Nmsrmn Eqlspace h_SUR Msrmn_Fitting global Msr_Mois Msr_Temp Msr_Time Tp_t Evap EPCT SAVEDTheta_LLT SAVEDTheta_LLh SAVEDTheta_UUh -global Ksoil Rl SMC Ztot DeltZ_R Theta_o rroot frac bbx wfrac RWUtot RWUtott RWUtottt Rls Tatot LR PSItot sfactortot LAI_msr R_depth RTB VPD_msr Tss Tsss SUMTIME Tsur FOC FOS FOSL MSOC Coef_Lamda fieldMC DELT Dur_tot Precipp fmax sitename +global Ksoil Rl SMC Ztot DeltZ_R Theta_o rroot frac bbx wfrac RWUtot RWUtott RWUtottt Rls Tatot LR PSItot sfactortot LAI_msr R_depth RTB VPD_msr Tss Tsss SUMTIME Tsur Precipp %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%% The time and domain information setting. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -KIT=0; % KIT is used to count the number of iteration in a time step; -NIT=30; % Desirable number of iterations in a time step; -Nmsrmn=Dur_tot*10; %Nmsrmn=140256*100; Here, it is made as big as possible, in case a long simulation period containing many time step is defined. -SUMTIME=0; -DURTN=DELT*Dur_tot; % Duration of simulation period; -KT=0; % Number of time steps; -TIME=0*DELT; % Time of simulation released; +KIT=0; % KIT is used to count the number of iteration in a time step; +NIT=30; % Desirable number of iterations in a time step; +Nmsrmn=Dur_tot*10; %Nmsrmn=140256*100; Here, it is made as big as possible, in case a long simulation period containing many time step is defined. +SUMTIME=0; +DURTN=DELT*Dur_tot; % Duration of simulation period; +KT=0; % Number of time steps; +TIME=0*DELT; % Time of simulation released; - -TEND=TIME+DURTN; % Time to be reached at the end of simulation period; -Delt_t=DELT; % Duration of time step [Unit of second] -Delt_t0=Delt_t; % Duration of last time step; -tS=DURTN/Delt_t; % Is the tS(time step) needed to be added with 1? + +TEND=TIME+DURTN; % Time to be reached at the end of simulation period; +Delt_t=DELT; % Duration of time step [Unit of second] +Delt_t0=Delt_t; % Duration of last time step; +tS=DURTN/Delt_t; % Is the tS(time step) needed to be added with 1? % Cause the start of simulation period is from 0mins, while the input data start from 30mins. - -xERR=0.02; % Maximum desirable change of moisture content; -hERR=0.1e08; % Maximum desirable change of matric potential; -TERR=2; % Maximum desirable change of temperature; + +xERR=0.02; % Maximum desirable change of moisture content; +hERR=0.1e08; % Maximum desirable change of matric potential; +TERR=2; % Maximum desirable change of temperature; PERR=5000; % Maximum desirable change of soil air pressure (Pa,kg.m^-1.s^-1); -uERR=0.02; % Maximum desirable change of total water content; -Tot_Depth=500; % Unit is cm. it should be usually bigger than 0.5m. Otherwise, - % the DeltZ would be reset in 50cm by hand; -R_depth=300; % -Eqlspace=0; % Indicator for deciding is the space step equal or not; +uERR=0.02; % Maximum desirable change of total water content; +Tot_Depth=500; % Unit is cm. it should be usually bigger than 0.5m. Otherwise, + % the DeltZ would be reset in 50cm by hand; +R_depth=300; % +Eqlspace=0; % Indicator for deciding is the space step equal or not; NL=100; -if ~Eqlspace - run Dtrmn_Z % Determination of NL, the number of elments; +if ~Eqlspace + run Dtrmn_Z % Determination of NL, the number of elments; else - for ML=1:NL - DeltZ(ML)=Tot_Depth/NL; - end -end - -NN=NL+1; % Number of nodes; -mN=NN+1; + for ML=1:NL + DeltZ(ML)=Tot_Depth/NL; + end +end + +NN=NL+1; % Number of nodes; +mN=NN+1; mL=NL+1; % Number of elements. Prevending the exceeds of size of arraies; -nD=2; -NS=1; % Number of soil types; -SAVE=zeros(3,3,3); % Arraies for calculating boundary flux; +nD=2; +NS=1; % Number of soil types; +SAVE=zeros(3,3,3); % Arraies for calculating boundary flux; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -global h_TE -global W_Chg +global h_TE +global W_Chg global l CKTN -global RHOL SSUR RHO_bulk Rv g RDA -global KL_h KL_T D_Ta +global RHOL SSUR RHO_bulk Rv g RDA +global KL_h KL_T D_Ta global Theta_L Theta_LL Se h hh T TT Theta_V h_frez hh_frez T_CRIT TT_CRIT global W WW MU_W f0 L_WT -global DhT +global DhT global GWT Gamma0 MU_W0 MU1 b W0 Gamma_w global Chh ChT Khh KhT Kha Vvh VvT Chg global C1 C2 C3 C4 C5 C6 C7 C9 global QL QL_D QL_disp RHS global HR RHOV_s RHOV DRHOV_sT DRHOVh DRHOVT global RHODA DRHODAt DRHODAz Xaa XaT Xah -global D_Vg D_V D_A +global D_Vg D_V D_A global k_g Sa V_A Alpha_Lg POR_C Eta global P_g P_gg Theta_g P_g0 Beta_g global MU_a fc Unit_C Hc UnitC global Cah CaT Caa Kah KaT Kaa Vah VaT Vaa Cag global Lambda1 Lambda2 Lambda3 c_L c_a c_V c_i L0 -global Lambda_eff c_unsat L LL Tr +global Lambda_eff c_unsat L LL Tr global CTh CTT CTa KTh KTT KTa VTT VTh VTa CTg CTT_PH CTT_LT CTT_g CTT_Lg EfTCON TETCON global Kcvh KcvT Kcva Ccvh CcvT Kcah KcaT Kcaa Ccah CcaT Ccaa @@ -76,14 +76,14 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% global MO Ta U Ts Zeta_MO % U_wind is the mean wind speed at height z_ref (m��s^-1), U is the wind speed at each time step. global Precip SH HR_a UseTs_msrmn % Notice that Evap and Precip have only one value for each time step. Precip needs to be measured in advance as the input. -global Gsc Sigma_E -global Rns Rnl -global Tmax Tmin Hrmax Hrmin Um +global Gsc Sigma_E +global Rns Rnl +global Tmax Tmin Hrmax Hrmin Um %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% Variables information for soil air pressure propagation +% Variables information for soil air pressure propagation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -global TopPg +global TopPg %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Fluxes information with different mechanisms @@ -94,8 +94,7 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Variable information for updating the state variables %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -global hOLD TOLD P_gOLD J -global porosity SaturatedMC ResidualMC Coefficient_n Coefficient_Alpha +global hOLD TOLD P_gOLD J %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Variables information for initialization subroutine %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -108,22 +107,22 @@ % Effective Thermal conductivity and capacity variables %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% global EHCAP ThmrlCondCap Evaptranp_Cal SWCC ThermCond -global Ts_a0 Ts_a Ts_b Ts_w -global Pg_w Pg_a0 Pg_a Pg_b +global Ts_a0 Ts_a Ts_b Ts_w +global Pg_w Pg_a0 Pg_a Pg_b global Hsur_w Hsur_a0 Hsur_a Hsur_b Rn VPD_msr LAI_msr %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Variables information for boundary condition settings %> NBCh Indicator for type of surface boundary condition on mass euqation to be applied; -%> "1"--Specified matric head; +%> "1"--Specified matric head; %> "2"--Specified potential moisture flux (potential evaporation rate of precipitation rate); %> "3"--Specified atmospheric forcing; %> NBCT Indicator for type of surface boundary condtion on energy equation to be applied; -%> "1"--Specified temperature; +%> "1"--Specified temperature; %> "2"--Specified heat flux; % "3"--Specified atmospheric forcing; %> NBCP Indicator for type of surface boundary condition on dry air equation to be applied; -%> "1"--Specified atmospheric pressure; -%> "2"--Specified atmospheric forcing (Measured atmospheric pressure data); +%> "1"--Specified atmospheric pressure; +%> "2"--Specified atmospheric forcing (Measured atmospheric pressure data); %> BCh Value of specified matric head (if NBCh=1) or specified potential moisture flux (if NBCh=2) at surface; %> BChB The same as BCh but at the bottom of column; %> BCT Value of specified temperature (if NBCT=1) or specified head flux (if NBCT=2) at surface; @@ -136,7 +135,7 @@ %> "2"--Specified moisture flux of BChB; %> "3"--Zero matric head gradient at bottom(Gravity drainage); %> NBCTB Type of boundary condition on temperature at bottom of column; -%> "1"--Specified temperature BCTB at bottom; +%> "1"--Specified temperature BCTB at bottom; %> "2"--Specified heat flux BCTB at bottom; %> "3"--Zero temperature gradient at bottom (advection only); %> NBCPB Type of boundary condition on soil air pressure; @@ -150,7 +149,7 @@ %> DSTMAX Depression storage capacity; %> EXCESS Excess of available water over infiltration rate for current time step,expressed as a rate; %> AVAIL Maximum rate at which water can be supplied to the soil from above during the current time step; -%> +%> %> global alpha_h bx Srt rwuef Ts_msr Tb_msr Theta_U Theta_UU Theta_I Theta_II KfL_h RHOI KfL_T Tbtm r_a_SOIL Rn_SOIL EVAP XCAP SAVEKfL_h global Ta_msr RH_msr Rn_msr WS_msr G_msr Pg_msr HourInput Rns_msr SH_msr LE_msr DRHOVZ DTDZ DhDZ Precip_msr LAI_msr DTheta_LLh SAVETheta_UU DTheta_UUh EVAPO %SAVEDTheta_UUh @@ -158,34 +157,34 @@ bx=zeros(mL,nD);SAVEKfL_h=zeros(mL,nD); Srt=zeros(mL,nD);DhDZ=zeros(mL,1);DTDZ=zeros(mL,1);DRHOVZ=zeros(mL,1); DTheta_LLh=zeros(mL,nD);DTheta_UUh=zeros(mL,nD);SAVETheta_UU=zeros(mL,nD);SAVEDTheta_UUh=zeros(mL,nD);SAVEDTheta_LLh=zeros(mL,nD);SAVEDTheta_LLT=zeros(mL,nD); Ratio_ice=zeros(mL,nD); -KL_h=zeros(mL,nD); % The hydraulic conductivity(m��s^-1); -KfL_h=zeros(mL,nD); % The hydraulic conductivity considering ice blockking effect(m��s^-1); -KfL_T=zeros(mL,nD); % The depression temperature controlled by ice(m^2��Cels^-1��s^-1); -KL_T=zeros(mL,nD); % The conductivity controlled by thermal gradient(m^2��Cels^-1��s^-1); -D_Ta=zeros(mL,nD); % The thermal dispersivity for soil water (m^2��Cels^-1��s^-1); -Theta_L=zeros(mL,nD); % The soil moisture at the start of current time step; -Theta_LL=zeros(mL,nD); % The soil moisture at the end of current time step; -Theta_U=zeros(mL,nD); % The total soil moisture(water+ice) at the start of current time step; -Theta_UU=zeros(mL,nD); % The total soil moisture at the end of current time step; -Theta_I=zeros(mL,nD); % The soil ice water content at the start of current time step; -Theta_II=zeros(mL,nD); % The soil ice water content at the end of current time step; -Se=zeros(mL,nD); % The saturation degree of soil moisture; +KL_h=zeros(mL,nD); % The hydraulic conductivity(m��s^-1); +KfL_h=zeros(mL,nD); % The hydraulic conductivity considering ice blockking effect(m��s^-1); +KfL_T=zeros(mL,nD); % The depression temperature controlled by ice(m^2��Cels^-1��s^-1); +KL_T=zeros(mL,nD); % The conductivity controlled by thermal gradient(m^2��Cels^-1��s^-1); +D_Ta=zeros(mL,nD); % The thermal dispersivity for soil water (m^2��Cels^-1��s^-1); +Theta_L=zeros(mL,nD); % The soil moisture at the start of current time step; +Theta_LL=zeros(mL,nD); % The soil moisture at the end of current time step; +Theta_U=zeros(mL,nD); % The total soil moisture(water+ice) at the start of current time step; +Theta_UU=zeros(mL,nD); % The total soil moisture at the end of current time step; +Theta_I=zeros(mL,nD); % The soil ice water content at the start of current time step; +Theta_II=zeros(mL,nD); % The soil ice water content at the end of current time step; +Se=zeros(mL,nD); % The saturation degree of soil moisture; h=zeros(mN,1); % The matric head at the start of current time step; hh=zeros(mN,1); % The matric head at the end of current time step; h_frez=zeros(mN,1); % The freeze depression head at the start of current time step; hh_frez=zeros(mN,1); % The freeze depression head at the end of current time step; XCAP=zeros(mN,1); -T=zeros(mN,1); % The soil temperature at the start of current time step; -TT=zeros(mN,1); % The soil temperature at the end of current time step; -T_CRIT=zeros(mN,1); % The soil ice critical temperature at the start of current time step; -TT_CRIT=zeros(mN,1); % The soil ice critical temperature at the start of current time step; +T=zeros(mN,1); % The soil temperature at the start of current time step; +TT=zeros(mN,1); % The soil temperature at the end of current time step; +T_CRIT=zeros(mN,1); % The soil ice critical temperature at the start of current time step; +TT_CRIT=zeros(mN,1); % The soil ice critical temperature at the start of current time step; EPCT=zeros(mN,1); -Theta_V=zeros(mL,nD); % Volumetric gas content; -W=zeros(mL,nD); % Differential heat of wetting at the start of current time step(J��kg^-1); +Theta_V=zeros(mL,nD); % Volumetric gas content; +W=zeros(mL,nD); % Differential heat of wetting at the start of current time step(J��kg^-1); WW=zeros(mL,nD); % Differential heat of wetting at the end of current time step(J��kg^-1); % Integral heat of wetting in individual time step(J��m^-2); %%%%%%%%%%%%%%% Notice: the formulation of this in 'CondL_Tdisp' is not a sure. %%%%%%%%%%%%%% MU_W=zeros(mL,nD); % Visocity of water(kg��m^?6?1��s^?6?1); -f0=zeros(mL,nD); % Tortusity factor [Millington and Quirk (1961)]; kg.m^2.s^-2.m^-2.kg.m^-3 +f0=zeros(mL,nD); % Tortusity factor [Millington and Quirk (1961)]; kg.m^2.s^-2.m^-2.kg.m^-3 L_WT=zeros(mL,nD); % Liquid dispersion factor in Thermal dispersivity(kg��m^-1��s^-1)=-------------------------- m^2 (1.5548e-013 m^2); DhT=zeros(mN,1); % Difference of matric head with respect to temperature; m. kg.m^-1.s^-1 RHS=zeros(mN,1); % The right hand side part of equations in '*_EQ' subroutine; @@ -252,8 +251,8 @@ CTT_Lg=zeros(mL,nD); % Storage coefficient in energy conservation equation related to liquid and gas; CTT_g=zeros(mL,nD); % Storage coefficient in energy conservation equation related to air; CTT_LT=zeros(mL,nD); % Storage coefficient in energy conservation equation related to liquid and temperature; -EfTCON=zeros(mL,nD); % Effective heat conductivity Johansen method; -TETCON=zeros(mL,nD); % Effective heat conductivity Johansen method; +EfTCON=zeros(mL,nD); % Effective heat conductivity Johansen method; +TETCON=zeros(mL,nD); % Effective heat conductivity Johansen method; L=zeros(mN,1); % The latent heat of vaporization at the beginning of the time step; LL=zeros(mN,1); % The latent heat of vaporization at the end of the time step; CTh=zeros(mL,nD); % Storage coefficient in energy conservation equation related to matric head; @@ -325,7 +324,7 @@ % DTDZ=zeros(mL,1); KLhBAR=zeros(mL,1); DEhBAR=zeros(mL,1); -KLTBAR=zeros(mL,1); +KLTBAR=zeros(mL,1); DTDBAR=zeros(mL,1); QLH=zeros(mL,1); QLT=zeros(mL,1); @@ -357,34 +356,34 @@ Tsur=zeros(Nmsrmn/10,1); Ztot=zeros(ML,1); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%%%%%% The indicators needs to be set before the running of this program %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%% The indicators needs to be set before the running of this program %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% J=1; % Indicator denotes the index of soil type for choosing soil physical parameters; % rwuef=1; HourInput=1; SWCC=1; % indicator for choose the soil water characteristic curve, =0, Clapp and Hornberger; =1, Van Gen; -Evaptranp_Cal=3; % Indicator denotes the method of estimating evapotranspiration; - % Value of 1 means the ETind method, otherwise, ETdir method; +Evaptranp_Cal=3; % Indicator denotes the method of estimating evapotranspiration; + % Value of 1 means the ETind method, otherwise, ETdir method; UseTs_msrmn=1; % Value of 1 means the measurement Ts would be used; Otherwise, 0; % -Msrmn_Fitting=1; % Value of 1 means the measurement data is used to fit the simulations; +Msrmn_Fitting=1; % Value of 1 means the measurement data is used to fit the simulations; Hystrs=0; % If the value of Hystrs is 1, then the hysteresis is considered, otherwise 0; % Thmrlefc=1; % Consider the isothermal water flow if the value is 0, otherwise 1; % Soilairefc=0; % The dry air transport is considered with the value of 1,otherwise 0; % hThmrl=1; % Value of 1, the special calculation of water capacity is used, otherwise 0; % h_TE=0; % Value of 1 means that the temperature dependence % - % of matric head would be considered.Otherwise,0; % + % of matric head would be considered.Otherwise,0; % W_Chg=1; % Value of 0 means that the heat of wetting would % - % be calculated by Milly's method��Otherwise,1. The % + % be calculated by Milly's method��Otherwise,1. The % % method of Lyle Prunty would be used; % ThmrlCondCap=1; %1; % The indicator for choosing Milly's effective thermal capacity and conductivity % - % formulation to verify the vapor and heat transport in extremly dry soil. % + % formulation to verify the vapor and heat transport in extremly dry soil. % % ThmrlCond=1; % The indicator for choosing effective thermal conductivity methods, 1= de vries method;2= Jonhansen methods ThermCond=1; % The indicator for choosing effective thermal conductivity methods, 1= de vries method;2= Jonhansen methods;3= Simplified de vries method(Tian 2016);4= Farouki methods % SWCC=1; % indicator for choose the soil water characteristic curve, =0, Clapp and Hornberger; =1, Van Gen; CHST=0; % Indicator of parameters derivation using soil texture or not. CHST=1, use; CHST=0 not use ISOC=1; % Indicator of considering soil organic matter effect or not. ISOC=1, yes; ISOC=0 no %%%%% 172 27%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -run soilpropertyread %load soil property + CKTN=(50+2.575*20); % Constant used in calculating viscosity factor for hydraulic conductivity l=0.5; % Coefficient in VG model; g=981; % Gravity acceleration (cm.s^-2); @@ -399,9 +398,9 @@ UnitC=100; % Change of meter into centimeter; Hc=0.02; % Henry's constant; GWT=7; % The gain factor(dimensionless),which assesses the temperature - % dependence of the soil water retention curve is set as 7 for + % dependence of the soil water retention curve is set as 7 for % sand (Noborio et al, 1996); -MU_a=1.846*10^(-4); % (g.cm^-1.s^-1)Viscosity of air (original 1.846*10^(-5)kg.m^-1.s^-1); +MU_a=1.846*10^(-4); % (g.cm^-1.s^-1)Viscosity of air (original 1.846*10^(-5)kg.m^-1.s^-1); Gamma0=71.89; % The surface tension of soil water at 25 Cels degree. (g.s^-2); Gamma_w=RHOL*g; % Specific weight of water(g.cm^-2.s^-2); Lambda1=0.228/UnitC;%-0.197/UnitC;% 0.243/UnitC; % Coefficients in thermal conductivity; @@ -421,32 +420,38 @@ Gsc=1360; % The solar constant (1360 W��m^-2) Sigma_E=4.90*10^(-9); % The stefan-Boltzman constant.(=4.90*10^(-9) MJ��m^-2��Cels^-4��d^-1) P_g0=95197.850;%951978.50; % The mean atmospheric pressure (Should be given in new simulation period subroutine.) -rroot=1.5*1e-3; +rroot=1.5*1e-3; RTB=1000; %initial root total biomass (g m-2) Precipp=0; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Input for producing initial soil moisture and soil temperature profile %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -Mdata=textread([InputPath, 'Mdata.txt']); -Ta_msr=Mdata(:,2)'; +fID = fopen([InputPath, 'Mdata.txt']); +Mdata = textscan(fID, ""); +fclose(fID); + +Ta_msr=Mdata{:,2}; +RH_msr=Mdata{:,3}; +WS_msr=Mdata{:,4}; +Pg_msr=Mdata{:,5}; +Rn_msr=Mdata{:,7}; +Rns_msr=Mdata{:,7}; +VPD_msr=Mdata{:,9}; +LAI_msr=Mdata{:,10}; +G_msr=Mdata{:,7}*0.15; +Ts_msr=Mdata{:,2}; +Precip_msr=Mdata{:,6}*10*DELT; + for jj=1:Dur_tot -if Ta_msr(jj)<-100 -Ta_msr(jj)=NaN; -end + if Ta_msr(jj)<-100 + Ta_msr(jj)=NaN; + end end -RH_msr=Mdata(:,3)'; -WS_msr=Mdata(:,4)'; -Pg_msr=Mdata(:,5)'; -Rn_msr=Mdata(:,7)'; -Rns_msr=Mdata(:,7)'; -VPD_msr=Mdata(:,9)'; -LAI_msr=Mdata(:,10)'; -G_msr=Mdata(:,7)'*0.15; -Ts_msr=Mdata(:,2)'; -Precip_msr=Mdata(:,6)'*10*DELT; Tmin=min(Ts_msr); + Precip_msr=Precip_msr.*(1-fmax.*exp(-0.5*0.5*Tot_Depth/100)); + % load initial soil moisture and soil temperature from ERA5 Initial_path=dir(fullfile(InitialConditionPath,[sitename,'*.nc'])); InitND1=5; % Unit of it is cm. These variables are used to indicated the depth corresponding to the measurement. @@ -455,7 +460,7 @@ InitND4=100; InitND5=200; InitND6=300; -if SWCC==0 +if SWCC==0 InitT0= -1.762; %-1.75estimated soil surface temperature-1.762 InitT1= -0.662; InitT2= 0.264; @@ -464,7 +469,7 @@ InitT5= 3.657;%; InitT6= 6.033; BtmT=6.62; %9 8.1 -InitX0= 0.088; +InitX0= 0.088; InitX1= 0.095; % Measured soil liquid moisture content InitX2= 0.180; %0.169 InitX3= 0.213; %0.205 @@ -481,7 +486,7 @@ InitT5= ncread([InitialConditionPath,Initial_path(5).name],'stl4')-273.15; InitT6= ncread([InitialConditionPath,Initial_path(5).name],'stl4')-273.15; Tss = InitT0; - if InitT0 < 0 || InitT1 < 0 || InitT2 < 0 || InitT3 < 0 || InitT4 < 0 || InitT5 < 0 || InitT6 < 0 + if InitT0 < 0 || InitT1 < 0 || InitT2 < 0 || InitT3 < 0 || InitT4 < 0 || InitT5 < 0 || InitT6 < 0 InitT0= 0; InitT1= 0; InitT2= 0; @@ -513,6 +518,6 @@ InitX4= fieldMC(4); %0.14335 InitX5= fieldMC(5); InitX6= fieldMC(6); -BtmX = fieldMC(6); +BtmX = fieldMC(6); end end \ No newline at end of file diff --git a/src/STEMMUS_SCOPE.m b/src/STEMMUS_SCOPE.m index de3d57f2..f2e676d3 100644 --- a/src/STEMMUS_SCOPE.m +++ b/src/STEMMUS_SCOPE.m @@ -1,12 +1,12 @@ %% STEMMUS-SCOPE.m (script) -% STEMMUS-SCOPE is a model for Integrated modeling of canopy photosynthesis, fluorescence, -% and the transfer of energy, mass, and momentum in the soil–plant–atmosphere continuum +% STEMMUS-SCOPE is a model for Integrated modeling of canopy photosynthesis, fluorescence, +% and the transfer of energy, mass, and momentum in the soil–plant–atmosphere continuum % % Version: 1.0.1 % % Copyright (C) 2021 Yunfei Wang, Lianyu Yu, Yijian Zeng, Christiaan Van der Tol, Bob Su -% Contact: y.wang-3@utwente.nl; l.yu@utwente.nl; y.zeng@utwente.nl; c.vandertol@utwente.nl; z.su@utwente.nl +% Contact: y.wang-3@utwente.nl; l.yu@utwente.nl; y.zeng@utwente.nl; c.vandertol@utwente.nl; z.su@utwente.nl % % This program is free software: you can redistribute it and/or modify % it under the terms of the GNU General Public License as published by @@ -26,37 +26,50 @@ % but there still global variables here, because we not sure which % progresses related to these global variables. +% Load in required Octave packages if STEMMUS-SCOPE is being run in Octave: +if exist('OCTAVE_VERSION', 'builtin') ~= 0 + pkg load netcdf + pkg load statistics +end + % Read the configPath file. Due to using MATLAB compiler, we cannot use run(CFG) global CFG if isempty(CFG) CFG = '../config_file_crib.txt'; end -disp (['Reading config from ',CFG]) -[DataPaths.soilProperty, DataPaths.input, DataPaths.output, ... - DataPaths.forcingPath, forcingFileName, numberOfTimeSteps, ... - DataPaths.initialCondition] = io.read_config(CFG); +disp (['Reading config from ', CFG]) +global InputPath OutputPath InitialConditionPath +[InputPath, OutputPath, InitialConditionPath] = io.read_config(CFG); -% Prepare forcing data +% Prepare forcing and soil data global IGBP_veg_long latitude longitude reference_height canopy_height sitename DELT Dur_tot -[SiteProperties, DELT, forcingTimeLength] = io.prepareForcingData(DataPaths, forcingFileName); -SoilPropertyPath = DataPaths.soilProperty; -InputPath = DataPaths.input; -OutputPath = DataPaths.output; -InitialConditionPath = DataPaths.initialCondition; -IGBP_veg_long = SiteProperties.igbpVegLong; +[SiteProperties, SoilProperties, TimeProperties] = io.prepareInputData(InputPath); +IGBP_veg_long = SiteProperties.IGBP_veg_long; latitude = SiteProperties.latitude; longitude = SiteProperties.longitude; -reference_height = SiteProperties.referenceHeight; -canopy_height = SiteProperties.canopyHeight; -sitename = SiteProperties.siteName; - -%Set the end time of the main loop in STEMMUS_SCOPE.m -%using config file or time length of forcing file -if isnan(numberOfTimeSteps) - Dur_tot=forcingTimeLength; -else - Dur_tot = min(numberOfTimeSteps, forcingTimeLength); -end +reference_height = SiteProperties.reference_height; +canopy_height = SiteProperties.canopy_height; +sitename = SiteProperties.sitename; + +DELT = TimeProperties.DELT; +Dur_tot = TimeProperties.Dur_tot; + +global SaturatedK SaturatedMC ResidualMC Coefficient_n Coefficient_Alpha +global porosity FOC FOS MSOC Coef_Lamda fieldMC fmax theta_s0 Ks0 +SaturatedK = SoilProperties.SaturatedK; +SaturatedMC = SoilProperties.SaturatedMC; +ResidualMC = SoilProperties.ResidualMC; +Coefficient_n = SoilProperties.Coefficient_n; +Coefficient_Alpha = SoilProperties.Coefficient_Alpha; +porosity = SoilProperties.porosity; +FOC = SoilProperties.FOC; +FOS = SoilProperties.FOS; +MSOC = SoilProperties.MSOC; +Coef_Lamda = SoilProperties.Coef_Lamda; +fieldMC = SoilProperties.fieldMC; +fmax = SoilProperties.fmax; +theta_s0 = SoilProperties.theta_s0; +Ks0 = SoilProperties.Ks0; %% run Constants %input soil parameters @@ -72,8 +85,8 @@ global SAVEKfL_h KCHK FCHK TKCHK hCHK TSAVEhh SAVEhh_frez Precip SAVETT INDICATOR thermal global Theta_g MU_a DeltZ Alpha_Lg global Beta_g KaT_Switch -global D_V D_A fc Eta nD POR -global ThmrlCondCap ZETA XK DVT_Switch +global D_V D_A fc Eta nD POR +global ThmrlCondCap ZETA XK DVT_Switch global MU_W Ks RHOL global Lambda1 Lambda2 Lambda3 RHO_bulk global RHODA RHOV c_a c_V c_L @@ -83,7 +96,7 @@ global hThmrl Tr COR IS Hystrs XWRE global Theta_V IH CORh XCAP Phi_s RHOI Lamda global W WW D_Ta SSUR -global W_Chg SWCC Imped Ratio_ice ThermCond Beta_gBAR Alpha_LgBAR +global W_Chg SWCC Imped Ratio_ice ThermCond Beta_gBAR Alpha_LgBAR global KLT_Switch xERR hERR TERR NBChB NBCT uERR global L TCON_dry TPS1 TPS2 TCON0 XSOC TCON_s global HCAP SF TCA GA1 GA2 GB1 GB2 HCD ZETA0 CON0 PS1 PS2 XWILT FEHCAP QMTT QMBB Evapo trap RnSOIL PrecipO @@ -102,12 +115,12 @@ if useXLSX == 0 % parameter_file = { 'setoptions.m', 'filenames.m', 'inputdata.txt'}; % Read parameter file which is 'input_data.xlsx' and return it as options. -% options = io.setOptions(parameter_file,path_input); +% options = io.setOptions(parameter_file,path_input); warning("the current stemmus-scope does not support useXLSX=0"); else - parameter_file = {'input_data.xlsx'}; - options = io.readStructFromExcel([path_input char(parameter_file)], 'options', 3, 1); -end + parameter_file = {'input_data.xlsx'}; + options = io.readStructFromExcel([path_input char(parameter_file)], 'options', 2, 1); +end if options.simulation>2 || options.simulation<0, fprintf('\n simulation option should be between 0 and 2 \r'); return, end @@ -116,7 +129,7 @@ if useXLSX==0 run([path_input parameter_file{2}]) else - [dummy,X] = xlsread([path_input char(parameter_file)],'filenames'); + [dummy,X] = xlsread([path_input char(parameter_file)],'filenames'); j = find(~strcmp(X(:,2),{''})); X = X(j,(1:end)); end @@ -154,7 +167,7 @@ 'I will use 0.25*Cab instead'); options.Cca_function_of_Cab = 1; else - + if ~(options.simulation==1) && (i==30 || i==32) warning('warning: input "', V(i).Name, '" not provided in input spreadsheet...', ... 'I will use the MODTRAN spectrum as it is'); @@ -185,7 +198,7 @@ end end end - + % the current stemmus-scope does not support useXLSX=0 if useXLSX==0 j2 = []; j1 = j+1; @@ -199,8 +212,8 @@ else V(i).Val = N(j2); end - - + + else if sum(~isnan(N(j,:)))<1 V(i).Val = -999; @@ -218,64 +231,64 @@ V(55).Val=mean(Ta_msr); %Input T parameters for different vegetation type sitename1=cellstr(sitename); -if strcmp(IGBP_veg_long(1:18)', 'Permanent Wetlands') +if strcmp(IGBP_veg_long(1:18)', 'Permanent Wetlands') V(14).Val= [0.2 0.3 288 313 328]; % These are five parameters specifying the temperature response. V(9).Val= [120]; % Vcmax, maximum carboxylation capacity (at optimum temperature) V(10).Val= [9]; % Ball-Berry stomatal conductance parameter V(11).Val= [0]; % Photochemical pathway: 0=C3, 1=C4 V(28).Val= [0.05]; % leaf width -elseif strcmp(IGBP_veg_long(1:19)', 'Evergreen Broadleaf') +elseif strcmp(IGBP_veg_long(1:19)', 'Evergreen Broadleaf') V(14).Val= [0.2 0.3 283 311 328]; V(9).Val= [80]; V(10).Val= [9]; V(11).Val= [0]; V(28).Val= [0.05]; -elseif strcmp(IGBP_veg_long(1:19)', 'Deciduous Broadleaf') +elseif strcmp(IGBP_veg_long(1:19)', 'Deciduous Broadleaf') V(14).Val= [0.2 0.3 283 311 328]; V(9).Val= [80]; V(10).Val= [9]; - V(11).Val= [0]; + V(11).Val= [0]; V(28).Val= [0.05]; -elseif strcmp(IGBP_veg_long(1:13)', 'Mixed Forests') +elseif strcmp(IGBP_veg_long(1:13)', 'Mixed Forests') V(14).Val= [0.2 0.3 281 307 328]; V(9).Val= [80]; V(10).Val= [9]; - V(11).Val= [0]; + V(11).Val= [0]; V(28).Val= [0.04]; -elseif strcmp(IGBP_veg_long(1:20)', 'Evergreen Needleleaf') +elseif strcmp(IGBP_veg_long(1:20)', 'Evergreen Needleleaf') V(14).Val= [0.2 0.3 278 303 328]; V(9).Val= [80]; V(10).Val= [9]; - V(11).Val= [0]; + V(11).Val= [0]; V(28).Val= [0.01]; -elseif strcmp(IGBP_veg_long(1:9)', 'Croplands') +elseif strcmp(IGBP_veg_long(1:9)', 'Croplands') if isequal(sitename1,{'ES-ES2'})||isequal(sitename1,{'FR-Gri'})||isequal(sitename1,{'US-ARM'})||isequal(sitename1,{'US-Ne1'}) V(14).Val= [0.2 0.3 278 303 328]; V(9).Val= [50]; V(10).Val= [4]; - V(11).Val= [1]; + V(11).Val= [1]; V(13).Val= [0.025]; % Respiration = Rdparam*Vcmcax V(28).Val= [0.03]; - else + else V(14).Val= [0.2 0.3 278 303 328]; V(9).Val= [120]; V(10).Val= [9]; - V(11).Val= [0]; - V(28).Val= [0.03]; + V(11).Val= [0]; + V(28).Val= [0.03]; end elseif strcmp(IGBP_veg_long(1:15)', 'Open Shrublands') V(14).Val= [0.2 0.3 288 313 328]; V(9).Val= [120]; V(10).Val= [9]; - V(11).Val= [0]; + V(11).Val= [0]; V(28).Val= [0.05]; -elseif strcmp(IGBP_veg_long(1:17)', 'Closed Shrublands') +elseif strcmp(IGBP_veg_long(1:17)', 'Closed Shrublands') V(14).Val= [0.2 0.3 288 313 328]; V(9).Val= [80]; V(10).Val= [9]; V(11).Val= [0]; V(28).Val= [0.05]; -elseif strcmp(IGBP_veg_long(1:8)', 'Savannas') +elseif strcmp(IGBP_veg_long(1:8)', 'Savannas') V(14).Val= [0.2 0.3 278 313 328]; V(9).Val= [120]; V(10).Val= [9]; @@ -287,13 +300,13 @@ V(10).Val= [9]; V(11).Val= [0]; V(28).Val= [0.03]; -elseif strcmp(IGBP_veg_long(1:9)', 'Grassland') +elseif strcmp(IGBP_veg_long(1:9)', 'Grassland') V(14).Val= [0.2 0.3 288 303 328]; if isequal(sitename1,{'AR-SLu'})||isequal(sitename1,{'AU-Ync'})||isequal(sitename1,{'CH-Oe1'})||isequal(sitename1,{'DK-Lva'})||isequal(sitename1,{'US-AR1'})||isequal(sitename1,{'US-AR2'})||isequal(sitename1,{'US-Aud'})||isequal(sitename1,{'US-SRG'}) V(9).Val= [120]; V(10).Val= [4]; V(11).Val= [1]; - V(13).Val= [0.025]; + V(13).Val= [0.025]; else V(9).Val= [120]; V(10).Val= [9]; @@ -417,16 +430,16 @@ atmo.M = helpers.aggreg(atmfile,spectral.SCOPEspec); %% 13. create output files -[Output_dir, f, fnames] = io.create_output_files_binary(parameter_file, sitename, path_of_code, path_input, path_output, spectral, options); +[Output_dir, fnames] = io.create_output_files_binary(parameter_file, sitename, path_of_code, path_input, path_output, spectral, options); run StartInit; % Initialize Temperature, Matric potential and soil air pressure. %% 14. Run the model -fprintf('\n The calculations start now \r') +disp('The calculations start now') calculate = 1; TIMEOLD=0;SAVEhh_frez=zeros(NN+1,1);FCHK=zeros(1,NN);KCHK=zeros(1,NN);hCHK=zeros(1,NN); TIMELAST=0; -SAVEtS=tS; kk=0; %DELT=Delt_t; +SAVEtS=tS; kk=0; %DELT=Delt_t; for i = 1:1:Dur_tot KT=KT+1; % Counting Number of timesteps if KT>1 && Delt_t>(TEND-TIME) @@ -454,7 +467,7 @@ TOLD_CRIT(MN)=T_CRIT(MN); T_CRIT(MN)=TT_CRIT(MN); %TTT_CRIT(MN,KT)=TT_CRIT(MN); - + hOLD(MN)=h(MN); h(MN)=hh(MN); %hhh(MN,KT)=hh(MN); @@ -503,20 +516,20 @@ else calculate = 1; end - + if calculate - + iter.counter = 0; - + LIDF_file = char(F(22).FileName); if ~isempty(LIDF_file) canopy.lidf = dlmread([path_input,'leafangles/',LIDF_file],'',3,0); else canopy.lidf = equations.leafangles(canopy.LIDFa,canopy.LIDFb); % This is 'ladgen' in the original SAIL model, end - - leafbio.emis = 1-leafbio.rho_thermal-leafbio.tau_thermal; - + + leafbio.emis = 1-leafbio.rho_thermal-leafbio.tau_thermal; + if options.calc_PSI fversion = @fluspect_B_CX; else @@ -526,15 +539,15 @@ leafopt = fversion(spectral,leafbio,optipar); leafbio.V2Z = 1; leafoptZ = fversion(spectral,leafbio,optipar); - + IwlP = spectral.IwlP; IwlT = spectral.IwlT; - + rho(IwlP) = leafopt.refl; tau(IwlP) = leafopt.tran; rlast = rho(nwlP); tlast = tau(nwlP); - + if options.soilspectrum == 0 rs(IwlP) = rsfile(:,soil.spectrum+1); else @@ -543,7 +556,7 @@ rs(IwlP) = BSM(soil,optipar,soilemp); end rslast = rs(nwlP); - + switch options.rt_thermal case 0 rho(IwlT) = ones(nwlT,1) * leafbio.rho_thermal; @@ -556,26 +569,26 @@ end leafopt.refl = rho; % extended wavelength ranges are stored in structures leafopt.tran = tau; - + reflZ = leafopt.refl; tranZ = leafopt.tran; reflZ(1:300) = leafoptZ.refl(1:300); tranZ(1:300) = leafoptZ.tran(1:300); leafopt.reflZ = reflZ; leafopt.tranZ = tranZ; - + soil.refl = rs; - + soil.Ts = meteo.Ta * ones(2,1); % initial soil surface temperature - + if length(F(4).FileName)>1 && options.simulation==0 atmfile = [path_input 'radiationdata/' char(F(4).FileName(k))]; atmo.M = helpers.aggreg(atmfile,spectral.SCOPEspec); end atmo.Ta = meteo.Ta; - + [rad,gap,profiles] = RTMo(spectral,atmo,soil,leafopt,canopy,angles,meteo,rad,options); - + switch options.calc_ebal case 1 [iter,fluxes,rad,thermal,profiles,soil,RWU,frac] ... @@ -591,15 +604,15 @@ if options.calc_xanthophyllabs [rad] = RTMz(spectral,rad,soil,leafopt,canopy,gap,angles,profiles); end - + if options.calc_planck rad = RTMt_planck(spectral,rad,soil,leafopt,canopy,gap,angles,thermal.Tcu,thermal.Tch,thermal.Ts(2),thermal.Ts(1),1); end - + if options.calc_directional directional = calc_brdf(options,directional,spectral,angles,rad,atmo,soil,leafopt,canopy,meteo,profiles,thermal); end - + otherwise Fc = (1-gap.Ps(1:end-1))'/nl; % Matrix containing values for Ps of canopy fluxes.aPAR = canopy.LAI*(Fc*rad.Pnh + equations.meanleaf(canopy,rad.Pnu , 'angles_and_layers',gap.Ps));% net PAR leaves @@ -621,42 +634,42 @@ else rad.Femtot = 1E3*leafbio.fqe* optipar.phi(spectral.IwlF) * fluxes.aPAR_Cab_eta; end - end + end end if options.simulation==2 && telmax>1, vi = helpers.count(nvars,vi,vmax,1); end - if KT==1 + if KT==1 if isreal(fluxes.Actot)&&isreal(thermal.Tsave)&&isreal(fluxes.lEstot)&&isreal(fluxes.lEctot) Acc=fluxes.Actot; lEstot =fluxes.lEstot; lEctot =fluxes.lEctot; - Tss=thermal.Tsave; + Tss=thermal.Tsave; else Acc=0; lEstot =0; lEctot =0; - Tss=Ta_msr(KT); + Tss=Ta_msr(KT); end elseif NoTime(KT)>NoTime(KT-1) if isreal(fluxes.Actot)&&isreal(thermal.Tsave)&&isreal(fluxes.lEstot)&&isreal(fluxes.lEctot) Acc=fluxes.Actot; lEstot =fluxes.lEstot; lEctot =fluxes.lEctot; - Tss=thermal.Tsave; + Tss=thermal.Tsave; else Acc=0; lEstot =0; lEctot =0; - Tss=Ta_msr(KT); + Tss=Ta_msr(KT); end end - + sfactortot(KT)=sfactor; - DSTOR0=DSTOR; - + DSTOR0=DSTOR; + if KT>1 run SOIL1 end - + for ML=1:NL %QVV(ML,KT)=QV(ML); %QLL(ML,KT)=QL(ML,1); @@ -729,7 +742,7 @@ end end run Forcing_PARM - + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% for KIT=1:NIT % Start the iteration procedure in a time step. [TT_CRIT,hh_frez]=HT_frez(hh,T0,g,L_f,TT,NN,hd,Tmin); @@ -760,15 +773,15 @@ DSTOR=min(EXCESS,DSTMAX); RS(KT)=(EXCESS-DSTOR)/Delt_t; end - + if Soilairefc==1 run Air_sub; end - + if Thmrlefc==1 run Enrgy_sub; end - + if max(CHK)<0.1 %&& max(FCHK)<0.001 %&& max(hCHK)<0.001 %&& min(KCHK)>0.001 break end @@ -806,7 +819,7 @@ hOLD(MN)=h(MN); h(MN)=hh(MN); %hhh(MN,KT)=hh(MN); - %HRA(MN,KT)=HR(MN); + %HRA(MN,KT)=HR(MN); if Thmrlefc==1 TOLD(MN)=T(MN); T(MN)=TT(MN); @@ -838,9 +851,14 @@ %SAVEDSTOR(KT)=DSTOR; end kk=k; - n_col = io.output_data_binary(f, k, xyt, rad, canopy, V, vi, vmax, options, fluxes, meteo, iter, thermal, spectral, gap, profiles, Sim_Theta_U, Sim_Temp, Trap, Evap); + + % Open files for writing + file_ids = structfun(@(x) fopen(x, 'a'), fnames, 'UniformOutput',false); + n_col = io.output_data_binary(file_ids, k, xyt, rad, canopy, V, vi, vmax, options, fluxes, meteo, iter, thermal, spectral, gap, profiles, Sim_Theta_U, Sim_Temp, Trap, Evap); + fclose("all"); end -fprintf('\n The calculations end now \r') + +disp('The calculations end now') if options.verify io.output_verification(Output_dir) end @@ -855,5 +873,5 @@ save([Output_dir,'output.mat']) %if options.makeplots % plot.plots(Output_dir) -%end +%end diff --git a/src/debug_Octave.m b/src/debug_Octave.m new file mode 100644 index 00000000..2186ee36 --- /dev/null +++ b/src/debug_Octave.m @@ -0,0 +1,2 @@ +config_file = "C:/STEMMUS_SCOPE_data/workdir/input/ZA-Kru_2022-09-30-1448/ZA-Kru_2022-09-30-1448_config.txt" +STEMMUS_SCOPE_exe(config_file); diff --git a/src/ebal.m b/src/ebal.m index d9a4289d..3cee7b8c 100644 --- a/src/ebal.m +++ b/src/ebal.m @@ -1,4 +1,4 @@ -function [iter,fluxes,rad,thermal,profiles,soil,RWU,frac] ... +function [iter,fluxes,rad,thermal,profiles,soil,RWU,frac] ... = ebal(iter,options,spectral,rad,gap,leafopt, ... angles,meteo,soil,canopy,leafbio,xyt,k,profiles,Delt_t) global Rl DeltZ Ks Theta_s Theta_r Theta_LL bbx NL KT sfactor PSItot sfactortot Theta_f @@ -27,7 +27,7 @@ % soil_respiration.m % % Table of contents of the function -% +% % 1. Initialisations for the iteration loop % intial values are attributed to variables % 2. Energy balance iteration loop @@ -38,29 +38,29 @@ % % The energy balance iteration loop works as follows: % -% RTMo More or less the classic SAIL model for Radiative +% RTMo More or less the classic SAIL model for Radiative % Transfer of sun and sky light (no emission by the vegetation) -% While continue Here an iteration loop starts to close the energy -% balance, i.e. to match the micro-meteorological model +% While continue Here an iteration loop starts to close the energy +% balance, i.e. to match the micro-meteorological model % and the radiative transfer model -% RTMt_sb A numerical Radiative Transfer Model for thermal +% RTMt_sb A numerical Radiative Transfer Model for thermal % radiation emitted by the vegetation -% resistances Calculates aerodynamic and boundary layer resistances +% resistances Calculates aerodynamic and boundary layer resistances % of vegetation and soil (the micro-meteorological model) -% biochemical Calculates photosynthesis, fluorescence and stomatal +% biochemical Calculates photosynthesis, fluorescence and stomatal % resistance of leaves (or biochemical_MD12: alternative) -% heatfluxes Calculates sensible and latent heat flux of soil and +% heatfluxes Calculates sensible and latent heat flux of soil and % vegetation -% Next soil heat flux is calculated, the energy balance -% is evaluated, and soil and leaf temperatures adjusted +% Next soil heat flux is calculated, the energy balance +% is evaluated, and soil and leaf temperatures adjusted % to force energy balance closure % end {while continue} -% -% meanleaf Integrates the fluxes over all leaf inclinations +% +% meanleaf Integrates the fluxes over all leaf inclinations % azimuth angles and layers, integrates over the spectrum % % usage: -%[iter,fluxes,rad,profiles,thermal] ... +%[iter,fluxes,rad,profiles,thermal] ... % = ebal(iter,options,spectral,rad,gap,leafopt, ... % angles,meteo,soil,canopy,leafbio) % @@ -68,7 +68,7 @@ % specified in a readme file. % % Input: -% +% % iter numerical parameters used in the iteration for energy balance closure % options calculation options % spectral spectral resolutions and wavelengths @@ -81,7 +81,7 @@ % leafbio leaf biochemical parameters % % Output: -% +% % iter numerical parameters used in the iteration for energy balance closure % fluxes energy balance, turbulent, and CO2 fluxes % rad radiation spectra @@ -100,7 +100,7 @@ CONT = 1; % is 0 when the calculation has finished es_fun = @(T)6.107*10.^(7.5.*T./(237.3+T)); s_fun = @(es, T) es*2.3026*7.5*237.3./(237.3+T).^2; -t = xyt.t(k); +t = xyt.t(k); Ta = meteo.Ta; ea = meteo.ea; Ca = meteo.Ca; @@ -112,7 +112,7 @@ else Deltat = Delt_t; end - x = [1:12;1:12]'*Deltat; + x = [1:12;1:12]'.*Deltat; Tsold = soil.Tsold; end @@ -174,31 +174,31 @@ %'Energy balance loop (Energy balance and radiative transfer) while CONT % while energy balance does not close - + % 2.1. Net radiation of the components % Thermal radiative transfer model for vegetation emission (with Stefan-Boltzman's equation) rad = RTMt_sb(spectral,rad,soil,leafopt,canopy,gap,angles,Tcu,Tch,Ts(2),Ts(1),1); % Add net radiation of (1) solar and sky and (2) thermal emission model - + Rnhct = rad.Rnhct; Rnuct = rad.Rnuct; Rnhst = rad.Rnhst; Rnust = rad.Rnust; - + Rnhc = rad.Rnhc; Rnuc = rad.Rnuc; Rnhs = rad.Rnhs; Rnus = rad.Rnus; - + Rnch = Rnhc + Rnhct; % Canopy (shaded) net radiation Rncu = Rnuc + Rnuct; % Canopy (sunlit) net radiation Rnsh = Rnhs + Rnhst; % Soil (shaded) net radiation Rnsu = Rnus + Rnust; % Soil (sunlit) net radiation Rns = [Rnsh Rnsu]'; % Soil (sun+sh) net radiation - + % 2.2. Aerodynamic roughness % calculate friction velocity [m s-1] and aerodynamic resistances [s m-1] - + resist_in.u = max(meteo.u,.2); resist_in.L = L; resist_in.LAI = canopy.LAI; @@ -211,16 +211,16 @@ resist_in.hc = canopy.hc; resist_in.w = canopy.leafwidth; resist_in.Cd = canopy.Cd; - + [resist_out] = resistances(resist_in); - + ustar = resist_out.ustar; raa = resist_out.raa; rawc = resist_out.rawc; raws = resist_out.raws; - + % 2.3. Biochemical processes - + % photosynthesis (A), fluorescence factor (F), and stomatal resistance (rcw), for shaded (1) and sunlit (h) leaves biochem_in.Fluorescence_model = options.Fluorescence_model; biochem_in.Type = leafbio.Type; @@ -229,7 +229,7 @@ biochem_in.BallBerry0 = leafbio.BallBerry0; biochem_in.O = meteo.Oa; biochem_in.Rdparam = leafbio.Rdparam; - + if options.Fluorescence_model==2 % specific for the v.Caemmerer-Magnani model b = @biochemical_MD12; biochem_in.Tyear = leafbio.Tyear; @@ -241,7 +241,7 @@ b = @biochemical; % specific for Berry-v.d.Tol model biochem_in.tempcor = options.apply_T_corr; biochem_in.Tparams = leafbio.Tparam; - biochem_in.stressfactor = SMCsf; + biochem_in.stressfactor = SMCsf; end % for shaded leaves @@ -250,7 +250,7 @@ biochem_in.Vcmo = fVh.*leafbio.Vcmo; biochem_in.Cs = Cch; biochem_in.Q = rad.Pnh_Cab*1E6; - + biochem_out = b(biochem_in); Ah = biochem_out.A; Ahh = biochem_out.Ag; @@ -259,16 +259,16 @@ rcwh = biochem_out.rcw; qEh = biochem_out.qE; % vCaemmerer- Magnani does not generate this parameter (dummy value) Knh = biochem_out.Kn; - + % for sunlit leaves biochem_in.T = Tcu; biochem_in.eb = ecu; biochem_in.Vcmo = fVu.*leafbio.Vcmo; biochem_in.Cs = Ccu; biochem_in.Q = rad.Pnu_Cab*1E6; - + biochem_out = b(biochem_in); - + Au = biochem_out.A; % Ag? or A? Auu = biochem_out.Ag; %GPP calculation. Ciu = biochem_out.Ci; @@ -276,17 +276,17 @@ rcwu = biochem_out.rcw; qEu = biochem_out.qE; Knu = biochem_out.Kn; - + Pinh = rad.Pnh; Pinu = rad.Pnu; Pinh_Cab = rad.Pnh_Cab; Pinu_Cab = rad.Pnu_Cab; - Rnh_PAR = rad.Rnh_PAR; + Rnh_PAR = rad.Rnh_PAR; Rnu_PAR = rad.Rnu_PAR; - + % 2.4. Fluxes (latent heat flux (lE), sensible heat flux (H) and soil heat flux G % in analogy to Ohm's law, for canopy (c) and soil (s). All in units of [W m-2] - + %soil.PSIs; rss = soil.rss; rac = (LAI+1)*(raa+rawc); @@ -295,7 +295,7 @@ [lEch,Hch,ech,Cch,lambdah,sh] = heatfluxes(rac,rcwh,Tch,ea,Ta,e_to_q,PSI,Ca,Cih,constants,es_fun,s_fun); [lEcu,Hcu,ecu,Ccu,lambdau,su] = heatfluxes(rac,rcwu,Tcu,ea,Ta,e_to_q,PSI,Ca,Ciu,constants,es_fun,s_fun); [lEs,Hs,~,~,lambdas,ss] = heatfluxes(ras,rss,Ts ,ea,Ta,e_to_q,PSIss,Ca,Ca,constants,es_fun,s_fun); - + %if any( ~isreal( Cch )) || any( ~isreal( Ccu(:) )) % error('Heatfluxes produced complex values for CO2 concentration!') %end @@ -322,7 +322,7 @@ BB2=AA2(~isinf(AA2)); PSI1 = (sum(BB1)-Trans)/sum(BB2); if isnan(PSI1) - PSI1 = -1; + PSI1 = -1; end if ~isreal(PSI1) PSI1 = -1; @@ -336,49 +336,49 @@ %%%%%%% if SoilHeatMethod==2 G = 0.30*Rns; - else - G = GAM/sqrt(pi)*2*sum(([Ts'; Tsold(1:end-1,:)] - Tsold)/Deltat .* (sqrt(x) - sqrt(x-Deltat))); + else + G = GAM/sqrt(pi)*2*sum(([Ts'; Tsold(1:end-1,:)] - Tsold)./Deltat .* (sqrt(x) - sqrt(x-Deltat))); G = G'; end % 2.5. Monin-Obukhov length L L = -rhoa*cp*ustar.^3.*(Ta+273.15)./(kappa*g*Htot); % [1] - L(L<-1E3) = -1E3; % [1] - L(L>1E2) = 1E2; % [1] - L(isnan(L)) = -1; % [1] - + L(L<-1E3) = -1E3; % [1] + L(L>1E2) = 1E2; % [1] + L(isnan(L)) = -1; % [1] + % 2.6. energy balance errors, continue criterion and iteration counter EBerch = Rnch -lEch -Hch; EBercu = Rncu -lEcu -Hcu; EBers = Rns -lEs -Hs - G; - + counter = counter+1; % Number of iterations maxEBercu = max(max(max(abs(EBercu)))); maxEBerch = max(abs(EBerch)); maxEBers = max(abs(EBers)); - - CONT = ( maxEBercu > maxEBer |... + + CONT = ( maxEBercu > maxEBer |... maxEBerch > maxEBer |... maxEBers > maxEBer) &... counter < maxit+1;% Continue iteration? if counter==5, Wc = 0.6; end if counter==10; Wc = 0.4; end - if counter==30; Wc = 0.1; end - - % 2.7. New estimates of soil (s) and leaf (c) temperatures, shaded (h) and sunlit (1) + if counter==30; Wc = 0.1; end + + % 2.7. New estimates of soil (s) and leaf (c) temperatures, shaded (h) and sunlit (1) Tch = Tch + Wc*EBerch./((rhoa*cp)./rac + rhoa*lambdah*e_to_q.*sh./(rac+rcwh)+ 4*leafbio.emis*sigmaSB*(Tch+273.15).^3); Tcu = Tcu + Wc*EBercu./((rhoa*cp)./rac + rhoa*lambdau*e_to_q.*su./(rac+rcwu)+ 4*leafbio.emis*sigmaSB*(Tcu+273.15).^3); - Ts = Ts + Wc*EBers./(rhoa*cp./ras + rhoa*lambdas*e_to_q.*ss/(ras+rss)+ 4*(1-soil.rs_thermal)*sigmaSB*(Ts+273.15).^3); % Ts contains shaded soil temperature and sunlit soil temperature + Ts = Ts + Wc*EBers./(rhoa*cp./ras + rhoa*lambdas*e_to_q.*ss/(ras+rss)+ 4*(1-soil.rs_thermal)*sigmaSB*(Ts+273.15).^3); % Ts contains shaded soil temperature and sunlit soil temperature Tch(abs(Tch)>100) = Ta; Tcu(abs(Tcu)>100) = Ta; Ts(abs(Ts)>100) = Ta; if (any(isnan(Tch)) || any(isnan(Tcu(:)))), warning('Canopy temperature gives NaNs'), end if any(isnan(Ts)), warning('Soil temperature gives NaNs'), end - + end iter.counter = counter; profiles.etah = Fh; -profiles.etau = Fu; +profiles.etau = Fu; if SoilHeatMethod<2 Tsold(2:end,:) = soil.Tsold(1:end-1,:); @@ -402,11 +402,11 @@ '\n Energy balance error sunlit vegetation = %4.2f W m-2 ', ... '\n Energy balance error shaded vegetation = %4.2f W m-2 ', ... '\n Energy balance error soil = %4.2f W m-2 '],maxEBercu, maxEBerch, maxEBers); - + end %% 4. Calculate the output per layer -if options.calc_vert_profiles +if options.calc_vert_profiles [Hcu1d ] = equations.meanleaf(canopy,Hcu, 'angles'); % [nli,nlo,nl] mean sens heat sunlit leaves [lEcu1d ] = equations.meanleaf(canopy,lEcu, 'angles'); % [nli,nlo,nl] mean latent sunlit leaves [Au1d ] = equations.meanleaf(canopy,Au, 'angles'); % [nli,nlo,nl] mean phots sunlit leaves @@ -416,7 +416,7 @@ %[Pnu1d_Cab ] = equations.meanleaf(canopy,Pinu_Cab, 'angles'); % [nli,nlo,nl] mean net radiation sunlit leaves [Rnu1d ] = equations.meanleaf(canopy,Rncu, 'angles'); % [nli,nlo,nl] mean net PAR sunlit leaves [Tcu1d ] = equations.meanleaf(canopy,Tcu, 'angles'); % [nli,nlo,nl] mean temp sunlit leaves - + profiles.Tchave = mean(Tch); % [1] mean temp shaded leaves profiles.Tch = Tch; % [nl] profiles.Tcu1d = Tcu1d; % [nl] @@ -430,10 +430,10 @@ %profiles.Pn1d_Cab = ((1-Ps(1:nl)).*Pinh_Cab + Ps(1:nl).*(Pnu1d_Cab)); %[nl] mean photos leaves, per layer profiles.Rn1d = ((1-Ps(1:nl)).*Rnch + Ps(1:nl).*(Rnu1d)); %[nl] end - + %% 5. Calculate spectrally integrated energy, water and CO2 fluxes -% sum of all leaves, and average leaf temperature +% sum of all leaves, and average leaf temperature % (note that averaging temperature is physically not correct...) Rnctot = LAI*(Fc*Rnch + equations.meanleaf(canopy,Rncu,'angles_and_layers',Ps)); % net radiation leaves @@ -445,7 +445,7 @@ Pntot = LAI*(Fc*Pinh + equations.meanleaf(canopy,Pinu,'angles_and_layers',Ps)); % net PAR leaves Pntot_Cab = LAI*(Fc*Pinh_Cab + equations.meanleaf(canopy,Pinu_Cab,'angles_and_layers',Ps)); % net PAR leaves Rntot_PAR = LAI*(Fc*Rnh_PAR + equations.meanleaf(canopy,Rnu_PAR, 'angles_and_layers',Ps));% net PAR leaves -aPAR_Cab_eta = LAI*(Fc*(profiles.etah .* Rnh_PAR) + equations.meanleaf(canopy,(profiles.etau .* Rnu_PAR), 'angles_and_layers',Ps)); +aPAR_Cab_eta = LAI*(Fc*(profiles.etah .* Rnh_PAR) + equations.meanleaf(canopy,(profiles.etau .* Rnu_PAR), 'angles_and_layers',Ps)); % ... green ePAR * relative fluorescence emission efficiency %%%%%%%%%%%%%%%%%%% [Delta_Rltot] = Root_properties(Actot,rroot); %%%%%%%%%%%%%%%%%%% Delta_Rl = fc*Delta_Rltot; @@ -506,7 +506,7 @@ thermal.Tsave = Tsave; % [oC] weighted average soil temperature thermal.raa = raa; % [s m-1] total aerodynamic resistance above canopy thermal.rawc = rawc; % [s m-1] aerodynamic resistance below canopy for canopy -thermal.raws = raws; % [s m-1] aerodynamic resistance below canopy for soil +thermal.raws = raws; % [s m-1] aerodynamic resistance below canopy for soil thermal.ustar = ustar; % [m s-1] friction velocity thermal.Tcu = Tcu; thermal.Tch = Tch; diff --git a/src/soilpropertyread.m b/src/soilpropertyread.m deleted file mode 100644 index 07e3163b..00000000 --- a/src/soilpropertyread.m +++ /dev/null @@ -1,143 +0,0 @@ -global SaturatedK SaturatedMC ResidualMC Coefficient_n Coefficient_Alpha porosity FOC FOS FOSL MSOC Coef_Lamda fieldMC latitude longitude fmax theta_s0 Ks0 sitename -% the path SoilPropertyPath is set in filereads.m -dirOutput=dir([SoilPropertyPath, 'Hydraul_Param_SoilGrids_Schaap_sl7.nc']); -%ncdisp([SoilPropertyPath,'Hydraul_Param_SoilGrids_Schaap_sl1.nc'],'/','full'); -%ncdisp([SoilPropertyPath,'Hydraul_Param_SoilGrids_Schaap_sl2.nc'],'/','full'); -%ncdisp([SoilPropertyPath,'Hydraul_Param_SoilGrids_Schaap_sl3.nc'],'/','full'); -%ncdisp([SoilPropertyPath,'Hydraul_Param_SoilGrids_Schaap_sl4.nc'],'/','full'); -%ncdisp([SoilPropertyPath,'Hydraul_Param_SoilGrids_Schaap_sl5.nc'],'/','full'); -%ncdisp([SoilPropertyPath,'Hydraul_Param_SoilGrids_Schaap_sl6.nc'],'/','full'); -%ncdisp([SoilPropertyPath,'Hydraul_Param_SoilGrids_Schaap_sl7.nc'],'/','full'); -%ncdisp([SoilPropertyPath,'CLAY1.nc','/','full'); -%% load soil property -if sitename(1:2)==['ID'] % Soil data missing at ID-Pag site, we use anothor location information here. - latitude=-1; - longitude=112; -end -lat=ncread([SoilPropertyPath,'CLAY1.nc'],'lat'); -lon=ncread([SoilPropertyPath,'CLAY1.nc'],'lon'); -for i=1:16800 - if abs(lat(i)-latitude)<0.0085 - break - end -end - for j=1:43200 - if abs(lon(j)-longitude)<0.0085 - break - end - end -depth1=ncread([SoilPropertyPath,'CLAY1.nc'],'depth'); -depth2=ncread([SoilPropertyPath,'CLAY2.nc'],'depth'); -depth3=ncread([SoilPropertyPath,'POR.nc'],'depth'); -CLAY1=ncread([SoilPropertyPath,'CLAY1.nc'],'CLAY',[j,i,1],[1,1,4]); -CLAY2=ncread([SoilPropertyPath,'CLAY2.nc'],'CLAY',[j,i,1],[1,1,4]); -SAND1=ncread([SoilPropertyPath,'SAND1.nc'],'SAND',[j,i,1],[1,1,4]); -SAND2=ncread([SoilPropertyPath,'SAND2.nc'],'SAND',[j,i,1],[1,1,4]); -SILT1=ncread([SoilPropertyPath,'SILT1.nc'],'SILT',[j,i,1],[1,1,4]); -SILT2=ncread([SoilPropertyPath,'SILT2.nc'],'SILT',[j,i,1],[1,1,4]); -OC1=ncread([SoilPropertyPath,'OC1.nc'],'OC',[j,i,1],[1,1,4]); -OC2=ncread([SoilPropertyPath,'OC2.nc'],'OC',[j,i,1],[1,1,4]); - -FOC=[CLAY1(1) CLAY1(3) CLAY2(1) CLAY2(2) CLAY2(3) CLAY2(4)]/100; %fraction of clay -FOS=[SAND1(1) SAND1(3) SAND2(1) SAND2(2) SAND2(3) SAND2(4)]/100; %fraction of sand -%FOSL=1-FOC-FOS; %fraction of silt -MSOC=double([OC1(1) OC1(3) OC2(1) OC2(2) OC2(3) OC2(4)])./10000; %mass fraction of soil organic matter -%% load lamda -lati=ncread([SoilPropertyPath,'lambda/lambda_l1.nc'],'lat'); -long=ncread([SoilPropertyPath,'lambda/lambda_l1.nc'],'lon'); -for i=1:21600 - if abs(lati(i)-latitude)<=0.0042 - break - end -end - for j=1:43200 - if abs(long(j)-longitude)<=0.0042 - break - end - end -lambda1=ncread([SoilPropertyPath,'lambda/lambda_l1.nc'],'lambda',[j,i],[1,1]); -lambda2=ncread([SoilPropertyPath,'lambda/lambda_l2.nc'],'lambda',[j,i],[1,1]); -lambda3=ncread([SoilPropertyPath,'lambda/lambda_l3.nc'],'lambda',[j,i],[1,1]); -lambda4=ncread([SoilPropertyPath,'lambda/lambda_l4.nc'],'lambda',[j,i],[1,1]); -lambda5=ncread([SoilPropertyPath,'lambda/lambda_l5.nc'],'lambda',[j,i],[1,1]); -lambda6=ncread([SoilPropertyPath,'lambda/lambda_l6.nc'],'lambda',[j,i],[1,1]); -lambda7=ncread([SoilPropertyPath,'lambda/lambda_l7.nc'],'lambda',[j,i],[1,1]); -lambda8=ncread([SoilPropertyPath,'lambda/lambda_l8.nc'],'lambda',[j,i],[1,1]); -Coef_Lamda=[lambda1 lambda3 lambda5 lambda6 lambda7 lambda8]; -%% load soil hydrulic parameters -lat=ncread([SoilPropertyPath,'Schaap/PTF_SoilGrids_Schaap_sl1_alpha.nc'],'latitude'); -lon=ncread([SoilPropertyPath,'Schaap/PTF_SoilGrids_Schaap_sl1_alpha.nc'],'longitude'); -% read data -for i=1:17924 - if abs(lat(i)-latitude)<=0.0042 - break - end -end - for j=1:43200 - if abs(lon(j)-longitude)<=0.0042 - break - end -end -% 0cm -alpha0=ncread([SoilPropertyPath,'Schaap/PTF_SoilGrids_Schaap_sl1_alpha.nc'],'alpha_0cm',[j,i],[1,1]); -n0=ncread([SoilPropertyPath,'Schaap/PTF_SoilGrids_Schaap_sl1_n.nc'],'n_0cm',[j,i],[1,1]); -theta_s0=ncread([SoilPropertyPath,'Schaap/PTF_SoilGrids_Schaap_sl1_thetas.nc'],'thetas_0cm',[j,i],[1,1]); -theta_r0=ncread([SoilPropertyPath,'Schaap/PTF_SoilGrids_Schaap_sl1_thetar.nc'],'thetar_0cm',[j,i],[1,1]); -Ks0=ncread([SoilPropertyPath,'Schaap/PTF_SoilGrids_Schaap_sl1_Ks.nc'],'Ks_0cm',[j,i],[1,1]); -% 5cm -alpha5=ncread([SoilPropertyPath,'Schaap/PTF_SoilGrids_Schaap_sl2_alpha.nc'],'alpha_5cm',[j,i],[1,1]); -n5=ncread([SoilPropertyPath,'Schaap/PTF_SoilGrids_Schaap_sl2_n.nc'],'n_5cm',[j,i],[1,1]); -theta_s5=ncread([SoilPropertyPath,'Schaap/PTF_SoilGrids_Schaap_sl2_thetas.nc'],'thetas_5cm',[j,i],[1,1]); -theta_r5=ncread([SoilPropertyPath,'Schaap/PTF_SoilGrids_Schaap_sl2_thetar.nc'],'thetar_5cm',[j,i],[1,1]); -Ks5=ncread([SoilPropertyPath,'Schaap/PTF_SoilGrids_Schaap_sl2_Ks.nc'],'Ks_5cm',[j,i],[1,1]); -% 15cm -alpha15=ncread([SoilPropertyPath,'Schaap/PTF_SoilGrids_Schaap_sl3_alpha.nc'],'alpha_15cm',[j,i],[1,1]); -n15=ncread([SoilPropertyPath,'Schaap/PTF_SoilGrids_Schaap_sl3_n.nc'],'n_15cm',[j,i],[1,1]); -theta_s15=ncread([SoilPropertyPath,'Schaap/PTF_SoilGrids_Schaap_sl3_thetas.nc'],'thetas_15cm',[j,i],[1,1]); -theta_r15=ncread([SoilPropertyPath,'Schaap/PTF_SoilGrids_Schaap_sl3_thetar.nc'],'thetar_15cm',[j,i],[1,1]); -Ks15=ncread([SoilPropertyPath,'Schaap/PTF_SoilGrids_Schaap_sl3_Ks.nc'],'Ks_15cm',[j,i],[1,1]); -% 30cm -alpha30=ncread([SoilPropertyPath,'Schaap/PTF_SoilGrids_Schaap_sl4_alpha.nc'],'alpha_30cm',[j,i],[1,1]); -n30=ncread([SoilPropertyPath,'Schaap/PTF_SoilGrids_Schaap_sl4_n.nc'],'n_30cm',[j,i],[1,1]); -theta_s30=ncread([SoilPropertyPath,'Schaap/PTF_SoilGrids_Schaap_sl4_thetas.nc'],'thetas_30cm',[j,i],[1,1]); -theta_r30=ncread([SoilPropertyPath,'Schaap/PTF_SoilGrids_Schaap_sl4_thetar.nc'],'thetar_30cm',[j,i],[1,1]); -Ks30=ncread([SoilPropertyPath,'Schaap/PTF_SoilGrids_Schaap_sl4_Ks.nc'],'Ks_30cm',[j,i],[1,1]); -% 60cm -alpha60=ncread([SoilPropertyPath,'Schaap/PTF_SoilGrids_Schaap_sl5_alpha.nc'],'alpha_60cm',[j,i],[1,1]); -n60=ncread([SoilPropertyPath,'Schaap/PTF_SoilGrids_Schaap_sl5_n.nc'],'n_60cm',[j,i],[1,1]); -theta_s60=ncread([SoilPropertyPath,'Schaap/PTF_SoilGrids_Schaap_sl5_thetas.nc'],'thetas_60cm',[j,i],[1,1]); -theta_r60=ncread([SoilPropertyPath,'Schaap/PTF_SoilGrids_Schaap_sl5_thetar.nc'],'thetar_60cm',[j,i],[1,1]); -Ks60=ncread([SoilPropertyPath,'Schaap/PTF_SoilGrids_Schaap_sl5_Ks.nc'],'Ks_60cm',[j,i],[1,1]); -% 100cm -alpha100=ncread([SoilPropertyPath,'Schaap/PTF_SoilGrids_Schaap_sl6_alpha.nc'],'alpha_100cm',[j,i],[1,1]); -n100=ncread([SoilPropertyPath,'Schaap/PTF_SoilGrids_Schaap_sl6_n.nc'],'n_100cm',[j,i],[1,1]); -theta_s100=ncread([SoilPropertyPath,'Schaap/PTF_SoilGrids_Schaap_sl6_thetas.nc'],'thetas_100cm',[j,i],[1,1]); -theta_r100=ncread([SoilPropertyPath,'Schaap/PTF_SoilGrids_Schaap_sl6_thetar.nc'],'thetar_100cm',[j,i],[1,1]); -Ks100=ncread([SoilPropertyPath,'Schaap/PTF_SoilGrids_Schaap_sl6_Ks.nc'],'Ks_100cm',[j,i],[1,1]); -% 200cm -alpha200=ncread([SoilPropertyPath,'Schaap/PTF_SoilGrids_Schaap_sl7_alpha.nc'],'alpha_200cm',[j,i],[1,1]); -n200=ncread([SoilPropertyPath,'Schaap/PTF_SoilGrids_Schaap_sl7_n.nc'],'n_200cm',[j,i],[1,1]); -theta_s200=ncread([SoilPropertyPath,'Schaap/PTF_SoilGrids_Schaap_sl7_thetas.nc'],'thetas_200cm',[j,i],[1,1]); -theta_r200=ncread([SoilPropertyPath,'Schaap/PTF_SoilGrids_Schaap_sl7_thetar.nc'],'thetar_200cm',[j,i],[1,1]); -Ks200=ncread([SoilPropertyPath,'Schaap/PTF_SoilGrids_Schaap_sl7_Ks.nc'],'Ks_200cm',[j,i],[1,1]); - -%% load maximum fractional saturated area - -FMAX=ncread([SoilPropertyPath,'surfdata.nc'],'FMAX'); -i = round((latitude + 90)*2) + 1; - -if longitude < 0 - j = round((longitude + 360)*2) + 1; -else - j = round(longitude*2) + 1; -end -fmax=FMAX(j,i); - -% soil property -SaturatedK=[Ks0/(3600*24) Ks5/(3600*24) Ks30/(3600*24) Ks60/(3600*24) Ks100/(3600*24) Ks200/(3600*24)];%[2.67*1e-3 1.79*1e-3 1.14*1e-3 4.57*1e-4 2.72*1e-4]; %[2.3*1e-4 2.3*1e-4 0.94*1e-4 0.94*1e-4 0.68*1e-4] 0.18*1e-4Saturation hydraulic conductivity (cm.s^-1); -SaturatedMC=[theta_s0 theta_s5 theta_s30 theta_s60 theta_s100 theta_s200]; % 0.42 0.55 Saturated water content; -ResidualMC=[theta_r0 theta_r5 theta_r30 theta_r60 theta_r100 theta_r200]; %0.037 0.017 0.078 The residual water content of soil; -Coefficient_n=[n0 n5 n30 n60 n100 n200]; %1.2839 1.3519 1.2139 Coefficient in VG model; -Coefficient_Alpha=[alpha0 alpha5 alpha30 alpha60 alpha100 alpha200]; % 0.02958 0.007383 Coefficient in VG model; -porosity=[theta_s0 theta_s5 theta_s30 theta_s60 theta_s100 theta_s200]; % Soil porosity; -fieldMC=(1./(((341.09.*Coefficient_Alpha).^(Coefficient_n)+1).^(1-1./Coefficient_n))).*(SaturatedMC-ResidualMC)+ResidualMC; \ No newline at end of file diff --git a/utils/csv_to_nc/README.md b/utils/csv_to_nc/README.md index 2fb64964..487ecaf0 100644 --- a/utils/csv_to_nc/README.md +++ b/utils/csv_to_nc/README.md @@ -1,56 +1,11 @@ # Converting `.csv` files to NetCDF files -Currently, model outputs are several files in `csv` format. The model output -should be converted to one netcedf file according to Plumber protocol. To do so, -there is a file -[Variables_will_be_in_NetCDF_file.csv](./Variables_will_be_in_NetCDF_file.csv. -The file lists variables that should be in the netcdf file. Also, there is a -python script [csv_to_nc.py](./csv_to_nc.py) that contains main fucntions. Below -we explain how to use the python scripts. - -## 1. Create Conda environment - -> We need to do this step only once. - -We download and install Conda: - -```sh -wget https://github.com/conda-forge/miniforge/releases/latest/download/Mambaforge-pypy3-Linux-x86_64.sh -bash Mambaforge-pypy3-Linux-x86_64.sh -b -p ~/mamba -``` - -Then, update base environment: - -```sh -. ~/mamba/bin/activate -mamba update --name base mamba -``` - -Finally, we create new conda environment called 'stemmus' with all required dependencies: - -```sh -cd STEMMUS_SCOPE/utils/csv_to_nc -mamba env create -``` - -## 2. Activate Conda environment - -> We need to do this step before running our python scripts. - -The environment can be activated with - -```sh -. ~/mamba/bin/activate stemmus -``` - -## 3. Run python script - -Open the configuration file [config_file_crib.txt][../../config_file_crib.txt] -or [config_file_snellius.txt][../../config_file_snellius.txt] and edit paths. Then, - -```sh -cd STEMMUS_SCOPE -python utils/csv_to_nc/generate_netcdf_files.py --config_file config_file_crib.txt --variable_file utils/csv_to_nc/Variables_will_be_in_NetCDF_file.csv -``` - -This will generate `ECdata.csv` and a netcdf file related to model output. \ No newline at end of file +The model outputs are expected to be in one NetCDF file according to [ALMA +conventions](https://web.lmd.jussieu.fr/~polcher/ALMA/). The file +[required_netcdf_variables.csv](./required_netcdf_variables.csv) +lists required variable names and their attributes based on [`ALMA+CF` +convention table](https://docs.google.com/spreadsheets/d/1CA3aTvI9piXqRqO-3MGrsH1vW-Sd87D8iZXHGrqK42o/edit#gid=2085475627). + +The MATLAB source code writes model outputs in `csv` format in the output +directory. The NetCDF file is generated using the module `save.py` from +[`PyStemmusScope`](https://pystemmusscope.readthedocs.io/en/latest/autoapi/index.html). diff --git a/utils/csv_to_nc/__init__.py b/utils/csv_to_nc/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/utils/csv_to_nc/csv_to_nc.py b/utils/csv_to_nc/csv_to_nc.py deleted file mode 100644 index 189297eb..00000000 --- a/utils/csv_to_nc/csv_to_nc.py +++ /dev/null @@ -1,245 +0,0 @@ -import pandas as pd - -from netCDF4 import Dataset, num2date -from pathlib import Path - -def generate_ec_data(forcing_path, forcing_name, output_path, duration_size): - """ - Read forcing data and create EC data in csv format. - """ - - # read data - filename = Path(forcing_path, forcing_name) # care for windows/unix paths - nc_fid = Dataset(filename, mode='r') - - # Extract variables as numpy arrays - t = nc_fid.variables['time'][:].flatten()/3600/24 - - Ta = nc_fid.variables['Tair'][:].flatten()-273.15 - - Rin = nc_fid.variables['SWdown'][:].flatten() - - u = nc_fid.variables['Wind'][:].flatten() - - Rli = nc_fid.variables['LWdown'][:].flatten() - - p = nc_fid.variables['Psurf'][:].flatten()/100 - - RH = nc_fid.variables['RH'][:].flatten() - - LAI = nc_fid.variables['LAI'][:].flatten() - - nctime = nc_fid.variables['time'] - ncdate = num2date(nctime[:],units= nctime.units, calendar=nctime.calendar) - year = [date.year for date in ncdate] - - Pre = nc_fid.variables['Precip'][:].flatten()/10 - - CO2air = nc_fid.variables['CO2air'][:].flatten()*44/22.4 - - Qair = nc_fid.variables['Qair'][:].flatten() - - data = [t, Ta, Rin, u, Rli, p, RH, LAI, year, Pre, CO2air, Qair] - names = ['t', 'Ta', 'Rin', 'u', 'Rli', 'p', 'RH', 'LAI', 'year', 'Pre', 'CO2air', 'Qair'] - - # Write to csv - df = pd.DataFrame.from_dict(dict(zip(names, data))) - - # subset data, if needed - if duration_size != 'NA': - var_t_length = int(duration_size) - df = df.loc[:var_t_length-1] - - filename = Path(output_path, "ECdata.csv") - df.to_csv(filename, index=False) - - # Extra information - lat = float(nc_fid.variables['latitude'][:].flatten()[0]) - lon = float(nc_fid.variables['longitude'][:].flatten()[0]) - return filename, lat, lon, nctime - - -def split(s): - t=s.split('"') - u=[t[i] for i in range(0, len(t), 2)] - v=[t[i] for i in range(1, len(t), 2)] - #u=[u[i][(i%2):(len(u[i])-(i+1)%2)] for i in range(len(u))] - u=[ - u[i][(i%2):(len(u[i])-(i+1)%2)] for i in range(len(u)-1) - ] + [u[i][(i%2):] for i in range(len(u)-1, len(u))] - w = [] - for i in range(len(u)): - w.extend(u[i].split(',')) - if i < len(v): - w.append(v[i]) - return w - - -def readcsv(filename, nrHeaderLines): - f = open(filename) - header = f.readline() - header = header.strip().split(',') - if nrHeaderLines > 1: # it is either 1 or 2 - f.readline() - content = f.readlines() - data = {} - for line in content: - line = split(line.strip()) - for i in range(len(header)): - if header[i] != '': - if header[i] not in data: - data[header[i]] = [] - data[header[i]].append(line[i]) - return data - - -def read_depths(filename): - f = open(filename) - depths = f.readline() - depths = depths.strip().strip('#').strip(',').split() # the first line has ,,,,,, at the end - depths = [float(depth) for depth in depths] - return depths - - -def read2d_transposed_unit(filename, nrHeaderLines, unit, depths): - f = open(filename) - f.readline() # skip the headerline(s) - if nrHeaderLines > 1: # it is either 1 or 2 - f.readline() - content = f.readlines() - data = [] - for line in content: - line = line.strip().split(',') - line = [float(l) for l in line] # convert this to float as we may want to scale it - if unit == 'K': - # Celsius to Kelvin : K = 273.15 + C - line = [273.15 + c for c in line] - elif unit == 'kg/m2': - # Yijian Zeng: m3/m3 to kg/m2: SM = VolumetricWaterContent * Density * Thickness - # VolumetricWaterContent: provided (m3/m3) - # Density: constant (water_density = 1000 kg per m3) - # Thickness (m): compute from depth (cm) - line = [(1000.0 * vwc * depth / 100.0) for vwc,depth in zip(line, depths)] - data.append(line) - return data - - -def generateNetCdf(lat, lon, nctime, output_dir, csvfiles_path, variables_filename, duration_size): - # Renamed radiation.dat to radiation.csv - # Renamed LEtot to lEtot - # Split AResist into AResist_rac and AResist_ras - # Renamed the 2nd occurence of SWdown and LWdown to SWdown_ec and LWdown_ex - # Note that the values in this Excel sheet file determine the metadata that the variables will receive - - # specify additional metadata here: - - additional_metadata = { - 'model': 'STEMMUS_SCOPE', - 'institution': 'University of Twente; Northwest A&F University', - 'contact': 'Zhongbo Su, z.su@utwente.nl; Yijian Zeng, y.zeng@utwente.nl; Yunfei Wang, y.wang-3@utwente.nl', - 'license_type': 'CC BY 4.0', - 'license_url': 'https://creativecommons.org/licenses/by/4.0/', - 'latitude': lat, - 'longitude': lon - } - - # Our CSV reader can't guess the number of header-lines, so this is hardcoded here: - sim_theta = Path(csvfiles_path, 'Sim_Theta.csv') - sim_temp = Path(csvfiles_path, 'Sim_Temp.csv') - - headerlines = {'aerodyn.csv': 2, 'ECdata.csv': 1, 'fluxes.csv': 2, 'radiation.csv': 2, 'Sim_Temp.csv': 2, 'Sim_Theta.csv': 2, 'surftemp.csv': 2} - - print(f'Reading variable metadata from {variables_filename}') - variables = readcsv(variables_filename, 1) - depths = read_depths(sim_temp) - - # Create a new empty netCDF file, in NETCDF3_CLASSIC format, just like the example file AU-Tum_2002-2017_OzFlux_Met.nc - filename = f"{Path(csvfiles_path).stem}_STEMMUS_SCOPE.nc" - filename_out = Path(output_dir, filename) - print(f"Creating {filename_out} ") - nc = Dataset(filename_out, mode='w', format='NETCDF3_CLASSIC') - - # Create the dimensions, as required by netCDF - - nc.createDimension('x', size=1) - nc.createDimension('y', size=1) - nc.createDimension('z', size=len(depths)) - nc.createDimension('time', None) - nc.createDimension('nchar', size=200) # this is not used, however the example file has it - - # Create the variables, as required by netCDF - - var_x = nc.createVariable('x', 'float64', ('x')) - var_y = nc.createVariable('y', 'float64', ('y')) - var_z = nc.createVariable('z', 'float64', ('z')) - var_t = nc.createVariable('time', 'float64', ('time')) - var_latitude = nc.createVariable('latitude', 'float32', ('y','x')) - var_latitude.setncattr('long_name', 'Gridbox latitude') - var_latitude.setncattr('standard_name', 'latitude') - var_latitude.setncattr('units', 'degrees') - var_latitude[:] = lat - var_longitude = nc.createVariable('longitude', 'float32', ('y','x')) - var_longitude.setncattr('long_name', 'Gridbox longitude') - var_longitude.setncattr('standard_name', 'longitude') - var_longitude.setncattr('units', 'degrees') - var_longitude[:] = lon - - # Add the generic metadata (taken from additional_metadata above) - - for metadata in additional_metadata: - nc.setncattr(metadata, additional_metadata[metadata]) - - # Fill the x, y, time variables with values - - var_x[:] = lon # in the example AU-Tum_2002-2017_OzFlux_Met.nc this is 1 - var_y[:] = lat # in the example AU-Tum_2002-2017_OzFlux_Met.nc this is 1 - var_z.setncattr('units', 'depth in cm') - var_z[:] = depths - var_t.setncattr('units', f'{nctime.units}') - var_t.setncattr('calendar', f'{nctime.calendar}') - - # Add all parameters as a netCDF variable; also add the known metadata (units, long_name, Stemmus_name, definition) - - data = {} - - for i in range(len(variables['short_name_alma'])): - variable = variables['short_name_alma'][i] - file = variables['File name'][i] - stemmusname = variables['Variable name in STEMMUS-SCOPE'][i] - unit = variables['unit'][i] - long_name = variables['long_name'][i] - standard_name = variables['standard_name'][i] - definition = variables['definition'][i] - dimensions = variables['dimension'][i] - var = None - if dimensions == 'XYT': - var = nc.createVariable(variable, 'float32', ('time','y','x')) - elif dimensions == 'XYZT': - var = nc.createVariable(variable, 'float32', ('time','y','x','z')) - var.setncattr('units', unit) - var.setncattr('long_name', long_name) - var.setncattr('standard_name', standard_name) - if stemmusname != '': - var.setncattr('STEMMUS-SCOPE_name', stemmusname) - if definition != '': - var.setncattr('definition', definition) - if stemmusname != '': - if file not in data: - print(f'Reading data from file: {file}') - data[file] = readcsv(Path(csvfiles_path, file), headerlines[file]) - var[:] = data[file][stemmusname] - else: # Sim_Temp or Sim_Theta - print(f'Reading data from file: {file}') - matrix = read2d_transposed_unit(Path(csvfiles_path, file), headerlines[file], unit, depths) - var[:] = matrix - - # Finally fill var_t with the nr of seconds per timestep - # Note: we don't take the numbers from the file, because Year + DoY is not as accurate (it becomes 3599.99, 7199.99 etc) - if duration_size != 'NA': - var_t_length = int(duration_size) - else: - var_t_length = len(nctime[:]) - var_t[:] = [i*1800 for i in range(var_t_length)] - - nc.close() - return filename_out \ No newline at end of file diff --git a/utils/csv_to_nc/environment.yml b/utils/csv_to_nc/environment.yml deleted file mode 100644 index ec60be6a..00000000 --- a/utils/csv_to_nc/environment.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: stemmus -channels: - - conda-forge -dependencies: - - python=3.9 - - netcdf4>=1.5.8,<2 - - pandas diff --git a/utils/csv_to_nc/generate_netcdf_files.py b/utils/csv_to_nc/generate_netcdf_files.py deleted file mode 100644 index 6d016dc8..00000000 --- a/utils/csv_to_nc/generate_netcdf_files.py +++ /dev/null @@ -1,49 +0,0 @@ -"""Script to generate netcdf file from model output csv files. - -python generate_netcdf_files.py --config_file config_file_crib.txt --variable_file Variables_will_be_in_NetCDF_file.csv - -""" -import argparse -import csv_to_nc - -from pathlib import Path - - -def cli(): - """Generate netcdf file from model output csv files.""" - parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument('--config_file', '-c', - default=Path(Path(__file__).parents[2],'config_file_crib.txt'), - help='Config file') - parser.add_argument('--variable_file', '-v', - default=Path(Path(__file__).parents[0],'Variables_will_be_in_NetCDF_file.csv'), - help='CSV file with variables that will be in NetCDF') - args = parser.parse_args() - - with open(args.config_file) as file: - config = dict(line.strip().split('=') for line in file) - - # get paths - output_path = config['OutputPath'] - forcing_path = config['ForcingPath'] - forcing_filename = config['ForcingFileName'] - - # get settings - duration_size = config['DurationSize'] - - # Find model output sub-directory linked to the forcing_filename - station_name = forcing_filename.split("_")[0] - model_output_dir = sorted(Path(output_path).glob(f"{station_name}*"))[-1] - - # Generate EC data from forcing data - ECdata_filename, lat, lon, nctime = csv_to_nc.generate_ec_data(forcing_path, forcing_filename, model_output_dir, duration_size) - print(f"{ECdata_filename}") - - # # Generate netcdf file from model output csv files - netcdf_filename = csv_to_nc.generateNetCdf(lat, lon, nctime, model_output_dir, model_output_dir, args.variable_file, duration_size) - print(f"Done writing {netcdf_filename}") - - -if __name__ == "__main__": - cli() - diff --git a/utils/csv_to_nc/Variables_will_be_in_NetCDF_file.csv b/utils/csv_to_nc/required_netcdf_variables.csv similarity index 97% rename from utils/csv_to_nc/Variables_will_be_in_NetCDF_file.csv rename to utils/csv_to_nc/required_netcdf_variables.csv index b26d3281..56950f25 100644 --- a/utils/csv_to_nc/Variables_will_be_in_NetCDF_file.csv +++ b/utils/csv_to_nc/required_netcdf_variables.csv @@ -1,27 +1,27 @@ -,pri_cmip,short_name_alma,short_name_cmip,standard_name,long_name,definition,unit,direction,dimension,grp_alma,grp_cmip,subgrid,Available in STEMMUS-SCOPE,File name,Variable name in STEMMUS-SCOPE, -1,1,SWnet,rss,surface_net_downward_shortwave_flux,Net shortwave radiation,"Incoming solar radiation less the simulated outgoing shortwave radiation, averaged over a grid cell",W/m2,Downward,XYT,,LEday,,Yes,radiation.csv,Netshort, -1,1,LWnet,rls,surface_net_downward_longwave_flux,Net longwave radiation,"Incident longwave radiation less the simulated outgoing longwave radiation, averaged over a grid cell",W/m2,Downward,XYT,,LEday,,Yes,radiation.csv,Netlong, -,2,SWdown,rsds,surface_downwelling_shortwave_flux_in_air,Downward short-wave radiation,,W/m2,Downward,XYT,,LEday,,Yes,radiation.csv,Rin, -,2,LWdown,rlds,surface_downwelling_longwave_flux_in_air,Downward long-wave radiation,,W/m2,Downward,XYT,,LEday,,Yes,radiation.csv,Rli, -,2,SWup,rsus,surface_upwelling_shortwave_flux_in_air,Upward short-wave radiation,,W/m2,Upward,XYT,,LEday,,Yes,radiation.csv,HemisOutShort, -2,2,LWup,rlus,surface_upwelling_longwave_flux_in_air,Upward long-wave radiation,This upward longwave flux is to be compared to an ISCCP derived product.,W/m2,Upward,XYT,,LEday,,Yes,radiation.csv,HemisOutLong, -1,1,Qle,hfls,surface_upward_latent_heat_flux,Latent heat flux,"Energy of evaporation, averaged over a grid cell",W/m2,Upward,XYT,,LEday,,Yes,fluxes.csv,lEtot, -1,1,Qh,hfss,surface_upward_sensible_heat_flux,Sensible heat flux,"Sensible energy, averaged over a grid cell",W/m2,Upward,XYT,,LEday,,Yes,fluxes.csv,Htot, -1,1,Qg,hfds,surface_downward_heat_flux,Ground heat flux,"Heat flux into the ground, averaged over a grid cell",W/m2,Downward,XYT,,LEday,,Yes,fluxes.csv,Gtot, -1,2,VegT,tcs,surface_canopy_skin_temperature,Vegetation Canopy Temperature,"Vegetation temperature, averaged over all vegetation types",K,-,XYT,,LEday,veg.,Yes,surftemp.csv,Tcave, -1,2,BaresoilT,tgs,surface_ground_skin_temperature,Temperature of bare soil,Surface bare soil temperature,K,-,XYT,,LEday,baresoil,Yes,surftemp.csv,Tsave, -2,1,SoilTemp,tsl,soil_temperature,Average layer soil temperature,Average soil temperature in each user-defined soil layer (3D variable),K,-,XYZT,,LEday,,Yes,Sim_Temp.csv,,"If soil layer thicknesses vary from one location to another, interpolate to a standard set of depths. Ideally, the interpolation should preserve the vertical integral." -1,1,SoilMoist,mrlsl,moisture_content_of_soil_layer,Average layer soil moisture,"Soil water content in each user-defined soil layer (3D variable). Includes the liquid, vapor and solid phases of water in the soil.",kg/m2,-,XYZT,,LWday,,Yes,Sim_Theta.csv,, -,2,AResist_rac,ares,aerodynamic_resistance,Aerodynamic resistance,,s/m,-,XYT,,LWday,,Yes,aerodyn.csv,rac, -,2,AResist_ras,ares,aerodynamic_resistance,Aerodynamic resistance,,s/m,-,XYT,,LWday,,Yes,aerodyn.csv,ras, -,1,RH,hur,relative_humidity,Relative humidity,,%,-,XYT,,LWday,,Yes,ECdata.csv,RH, -1,1,GPP,gpp,gross_primary_productivity_of_carbon,Gross Primary Production,Carbon Mass Flux out of Atmosphere due to Gross Primary Production on Land,kg/m2/s,Downward,XYT,,LCmon,,Yes,fluxes.csv,GPP, -1,1,SWdown_ec,rsds,surface_downwelling_shortwave_flux_in_air,Downward short-wave radiation,,W/m2,Downward,XYT,,L3hr,,,ECdata.csv,Rin, -1,1,LWdown_ec,rlds,surface_downwelling_longwave_flux_in_air,Downward long-wave radiation,,W/m2,Downward,XYT,,L3hr,,,ECdata.csv,Rli, -1,1,Qair,huss,specific_humidity,Near surface specific humidity,,kg/kg,-,XYT,,L3hr,,,ECdata.csv,Qair, -1,1,Tair,ta,air_temperature,Near surface air temperature,,K,-,XYT,,L3hr,,,ECdata.csv,Ta, -1,1,Psurf,ps,surface_air_pressure,Surface Pressure,,Pa,-,XYT,,L3hr,,,ECdata.csv,p, -2,1,Wind,ws,wind_speed,Near surface wind speed,,m/s,-,XYT,,L3hr,,,ECdata.csv,u, -,,Precip,pr,precipitation_flux,Precipitation rate,,kg/m2/s,Downward,XYT,,L3hr,,,ECdata.csv,Pre, -1,1,NEE,nep,surface_net_downward_mass_flux_of_carbon_dioxide_expressed_as_carbon_due_to_all_land_processes_excluding_anthropogenic_land_use_change,Net Ecosystem Exchange,Net Carbon Mass Flux out of Atmophere due to Net Ecosystem Productivity on Land.,kg/m2/s,Downward,XYT,,LCmon,,Yes,fluxes.csv,NEE, -1,1,Rnet,rss,surface_net_radiation_flux,Net radiation,"Net shortwave radiation less the simulated net longwave radiation, averaged over a grid cell",W/m2,Downward,XYT,,LEday,,Yes,radiation.csv,Rntot, +,pri_cmip,short_name_alma,short_name_cmip,standard_name,long_name,definition,unit,direction,dimension,grp_alma,grp_cmip,subgrid,Available in STEMMUS-SCOPE,file_name_STEMMUS-SCOPE,short_name_STEMMUS-SCOPE, +1,1,SWnet,rss,surface_net_downward_shortwave_flux,Net shortwave radiation,"Incoming solar radiation less the simulated outgoing shortwave radiation, averaged over a grid cell",W/m2,Downward,XYT,,LEday,,Yes,radiation.csv,Netshort, +1,1,LWnet,rls,surface_net_downward_longwave_flux,Net longwave radiation,"Incident longwave radiation less the simulated outgoing longwave radiation, averaged over a grid cell",W/m2,Downward,XYT,,LEday,,Yes,radiation.csv,Netlong, +,2,SWdown,rsds,surface_downwelling_shortwave_flux_in_air,Downward short-wave radiation,,W/m2,Downward,XYT,,LEday,,Yes,radiation.csv,Rin, +,2,LWdown,rlds,surface_downwelling_longwave_flux_in_air,Downward long-wave radiation,,W/m2,Downward,XYT,,LEday,,Yes,radiation.csv,Rli, +,2,SWup,rsus,surface_upwelling_shortwave_flux_in_air,Upward short-wave radiation,,W/m2,Upward,XYT,,LEday,,Yes,radiation.csv,HemisOutShort, +2,2,LWup,rlus,surface_upwelling_longwave_flux_in_air,Upward long-wave radiation,This upward longwave flux is to be compared to an ISCCP derived product.,W/m2,Upward,XYT,,LEday,,Yes,radiation.csv,HemisOutLong, +1,1,Qle,hfls,surface_upward_latent_heat_flux,Latent heat flux,"Energy of evaporation, averaged over a grid cell",W/m2,Upward,XYT,,LEday,,Yes,fluxes.csv,lEtot, +1,1,Qh,hfss,surface_upward_sensible_heat_flux,Sensible heat flux,"Sensible energy, averaged over a grid cell",W/m2,Upward,XYT,,LEday,,Yes,fluxes.csv,Htot, +1,1,Qg,hfds,surface_downward_heat_flux,Ground heat flux,"Heat flux into the ground, averaged over a grid cell",W/m2,Downward,XYT,,LEday,,Yes,fluxes.csv,Gtot, +1,2,VegT,tcs,surface_canopy_skin_temperature,Vegetation Canopy Temperature,"Vegetation temperature, averaged over all vegetation types",K,-,XYT,,LEday,veg.,Yes,surftemp.csv,Tcave, +1,2,BaresoilT,tgs,surface_ground_skin_temperature,Temperature of bare soil,Surface bare soil temperature,K,-,XYT,,LEday,baresoil,Yes,surftemp.csv,Tsave, +2,1,SoilTemp,tsl,soil_temperature,Average layer soil temperature,Average soil temperature in each user-defined soil layer (3D variable),K,-,XYZT,,LEday,,Yes,Sim_Temp.csv,,"If soil layer thicknesses vary from one location to another, interpolate to a standard set of depths. Ideally, the interpolation should preserve the vertical integral." +1,1,SoilMoist,mrlsl,moisture_content_of_soil_layer,Average layer soil moisture,"Soil water content in each user-defined soil layer (3D variable). Includes the liquid, vapor and solid phases of water in the soil.",kg/m2,-,XYZT,,LWday,,Yes,Sim_Theta.csv,, +,2,AResist_rac,ares,aerodynamic_resistance,Aerodynamic resistance,,s/m,-,XYT,,LWday,,Yes,aerodyn.csv,rac, +,2,AResist_ras,ares,aerodynamic_resistance,Aerodynamic resistance,,s/m,-,XYT,,LWday,,Yes,aerodyn.csv,ras, +,1,RH,hur,relative_humidity,Relative humidity,,%,-,XYT,,LWday,,Yes,ECdata.csv,RH, +1,1,GPP,gpp,gross_primary_productivity_of_carbon,Gross Primary Production,Carbon Mass Flux out of Atmosphere due to Gross Primary Production on Land,kg/m2/s,Downward,XYT,,LCmon,,Yes,fluxes.csv,GPP, +1,1,SWdown_ec,rsds,surface_downwelling_shortwave_flux_in_air,Downward short-wave radiation,,W/m2,Downward,XYT,,L3hr,,,ECdata.csv,Rin, +1,1,LWdown_ec,rlds,surface_downwelling_longwave_flux_in_air,Downward long-wave radiation,,W/m2,Downward,XYT,,L3hr,,,ECdata.csv,Rli, +1,1,Qair,huss,specific_humidity,Near surface specific humidity,,kg/kg,-,XYT,,L3hr,,,ECdata.csv,Qair, +1,1,Tair,ta,air_temperature,Near surface air temperature,,K,-,XYT,,L3hr,,,ECdata.csv,Ta, +1,1,Psurf,ps,surface_air_pressure,Surface Pressure,,Pa,-,XYT,,L3hr,,,ECdata.csv,p, +2,1,Wind,ws,wind_speed,Near surface wind speed,,m/s,-,XYT,,L3hr,,,ECdata.csv,u, +,,Precip,pr,precipitation_flux,Precipitation rate,,kg/m2/s,Downward,XYT,,L3hr,,,ECdata.csv,Pre, +1,1,NEE,nep,surface_net_downward_mass_flux_of_carbon_dioxide_expressed_as_carbon_due_to_all_land_processes_excluding_anthropogenic_land_use_change,Net Ecosystem Exchange,Net Carbon Mass Flux out of Atmophere due to Net Ecosystem Productivity on Land.,kg/m2/s,Downward,XYT,,LCmon,,Yes,fluxes.csv,NEE, +1,1,Rnet,rss,surface_net_radiation_flux,Net radiation,"Net shortwave radiation less the simulated net longwave radiation, averaged over a grid cell",W/m2,Downward,XYT,,LEday,,Yes,radiation.csv,Rntot,