diff --git a/projects2020/group05/.dockerignore b/projects2020/group05/.dockerignore new file mode 100644 index 00000000..adbb97d2 --- /dev/null +++ b/projects2020/group05/.dockerignore @@ -0,0 +1 @@ +data/ \ No newline at end of file diff --git a/projects2020/group05/.gitignore b/projects2020/group05/.gitignore new file mode 100644 index 00000000..a48b003d --- /dev/null +++ b/projects2020/group05/.gitignore @@ -0,0 +1,8 @@ +data +*.dat +*.json +*.pyc +__pycache__ +.pytest_cache +.gt_cache +.vscode \ No newline at end of file diff --git a/projects2020/group05/Dockerfile b/projects2020/group05/Dockerfile new file mode 100644 index 00000000..d86daf2a --- /dev/null +++ b/projects2020/group05/Dockerfile @@ -0,0 +1,76 @@ +FROM ubuntu:20.04 + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update \ + && apt-get install -y \ + apt-utils \ + sudo \ + build-essential \ + gcc \ + g++ \ + gfortran \ + gdb \ + wget \ + curl \ + tar \ + git \ + vim \ + make \ + cmake \ + cmake-curses-gui \ + python3-pip \ + python3-dev \ + libssl-dev \ + libboost-all-dev \ + libnetcdf-dev \ + libnetcdff-dev + +RUN update-alternatives --install /usr/bin/python python /usr/bin/python3 10 && \ + update-alternatives --install /usr/bin/pip pip /usr/bin/pip3 10 && \ + update-alternatives --set python /usr/bin/python3 && \ + update-alternatives --set pip /usr/bin/pip3 + +# set TZ +ENV TZ=US/Pacific +RUN echo $TZ > /etc/timezone && \ + dpkg-reconfigure --frontend noninteractive tzdata + +# install serialbox from source +RUN git clone --single-branch --branch savepoint_as_string https://github.com/VulcanClimateModeling/serialbox2.git /serialbox +RUN cd /serialbox && \ + mkdir build && \ + cd build && \ + cmake -DCMAKE_INSTALL_PREFIX=/usr/local/serialbox -DCMAKE_BUILD_TYPE=Release \ + -DSERIALBOX_USE_NETCDF=ON -DSERIALBOX_ENABLE_FORTRAN=ON \ + -DSERIALBOX_TESTING=ON ../ && \ + make -j8 && \ + make test && \ + make install && \ + /bin/rm -rf /serialbox + +# gt4py +RUN pip install git+https://github.com/gridtools/gt4py.git \ + && python -m gt4py.gt_src_manager install + +# add default user +ARG USER=user +ENV USER ${USER} +RUN useradd -ms /bin/bash ${USER} \ + && echo "${USER} ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers +ENV USER_HOME /home/${USER} +RUN chown -R ${USER}:${USER} ${USER_HOME} + +# create working directory +ARG WORKDIR=/work +ENV WORKDIR ${WORKDIR} +RUN mkdir ${WORKDIR} +RUN chown -R ${USER}:${USER} ${WORKDIR} + +WORKDIR ${WORKDIR} +USER ${USER} + +ENV PYTHONPATH="/usr/local/serialbox/python:${PYTHONPATH}" + +CMD ["/bin/bash"] + diff --git a/projects2020/group05/README.md b/projects2020/group05/README.md new file mode 100644 index 00000000..24c3aadc --- /dev/null +++ b/projects2020/group05/README.md @@ -0,0 +1,57 @@ +# A Python Implementation of GFS Scale-Aware Mass-Flux Shallow Convection Scheme Module +## Package `shalconv` structure +- `__init__.py`: configuration +- `funcphys.py`: thermodynamic functions +- `physcons.py`: constants +- `samfaerosols.py`: aerosol processes +- `samfshalcnv.py`: shallow convection scheme +- `serialization.py`: serialization +- `kernels/stencils_*.py`: GT4Py stencils of the shallow convection scheme +- `kernels/utils.py`: useful functions for GT4Py arrays + +## Unit tests +- `analyse_xml.py`: dependency analysis of fortran code +- `read_serialization.py`: read serialization data for unit tests +- `run_serialization.py`: generate serialization for unit tests +- `test_fpvsx.py`: test fpvsx function +- `test_part1.py`: test part1 of shallow convection scheme +- `test_part2.py`: test part2 of shallow convection scheme +- `test_part34.py`: test part3 and part4 of shallow convection scheme + +## Other files +- `build.sh`: script for building environment as docker image +- `enter.sh`: script for entering the docker environment +- `env_daint`: script for setting up environment in Piz Daint +- `submit_job.sh`: script for submitting SLURM jobs in Piz Daint of benchmarking shalconv scheme with gtcuda and gtx86 backends +- `get_data.sh`: download serialized data +- `main.py`: validation for shallow convection scheme +- `benchmark.py`: benchmark shallow convection scheme with various number of columns (ix) +- `plot.py`: plot benchmark results (already hardcoded) + +## Storage in GT4Py +All the arrays are broadcasted or sliced to the shape (1, ix, km) due to restrictions of gt4py stencil. +Operations applied to 1D array of shape (1, ix, 1) are propagated forward and then backward to keep consistency. + +## Configuration +`shalconv/__init__.py` specifies several configurations needed to run the scheme, including location of serialization data, backend type, +verbose output and floating/integer number type. One can also specify backend type by setting the environment variable `GT4PY_BACKEND` to be +one of `numpy`, `debug`, `gtx86`, `gtcuda`. + +## Build with docker in Linux +execute `build.sh` then `enter.sh`. + +## Build with docker in Windows +1. download serialized data and extract them according to `get_data.sh` +2. execute `docker build -t hpc4wc_project .` +3. execute `docker run -i -t --rm --mount type=bind,source={ABSOLUTE PATH OF THIS FOLDER},target=/work --name=hpc4wc_project hpc4wc_project` +4. execute `ipython main.py` or `benchmark.py` + +## Run on Piz Diant +1. execute `get_data.sh` to get serialized data +2. CHANGE `ISDOCKER` to False and `DATAPATH` in `shalconv/__init__.py` +3. execute `source env_diant` +4. execute `ipython main.py` or `benchmark.py` + +## Tests +Inside tests folder, execute `ipython run_serialization.py` to generate serialization +data needed for tests, then execute `ipython test_*.py` to run tests. diff --git a/projects2020/group05/benchmark.py b/projects2020/group05/benchmark.py new file mode 100644 index 00000000..d0ea5696 --- /dev/null +++ b/projects2020/group05/benchmark.py @@ -0,0 +1,135 @@ +from shalconv.serialization import read_random_input, read_data, numpy_dict_to_gt4py_dict, scalar_vars +from shalconv.samfshalcnv import samfshalcnv_func +from shalconv import DTYPE_INT, BACKEND, ISDOCKER +from time import time +import numpy as np +import numpy.f2py, os + + +def carray2fortranarray(data_dict): + + for key in data_dict: + if isinstance(data_dict[key], np.ndarray): + data_dict[key] = np.asfortranarray(data_dict[key]) + + return data_dict + + +def samfshalcnv_fort(data_dict): + + im = data_dict["im"] + ix = data_dict["ix"] + km = data_dict["km"] + delt = data_dict["delt"] + itc = data_dict["itc"] + ntc = data_dict["ntc"] + ntk = data_dict["ntk"] + ntr = data_dict["ntr"] + delp = data_dict["delp"] + prslp = data_dict["prslp"] + psp = data_dict["psp"] + phil = data_dict["phil"] + qtr = data_dict["qtr"][:,:,:ntr+2] + q1 = data_dict["q1"] + t1 = data_dict["t1"] + u1 = data_dict["u1"] + v1 = data_dict["v1"] + rn = data_dict["rn"] + kbot = data_dict["kbot"] + ktop = data_dict["ktop"] + kcnv = data_dict["kcnv"] + islimsk = data_dict["islimsk"] + garea = data_dict["garea"] + dot = data_dict["dot"] + ncloud = data_dict["ncloud"] + hpbl = data_dict["hpbl"] + ud_mf = data_dict["ud_mf"] + dt_mf = data_dict["dt_mf"] + cnvw = data_dict["cnvw"] + cnvc = data_dict["cnvc"] + clam = data_dict["clam"] + c0s = data_dict["c0s"] + c1 = data_dict["c1"] + pgcon = data_dict["pgcon"] + asolfac = data_dict["asolfac"] + + import shalconv_fortran + shalconv_fortran.samfshalconv_benchmark.samfshalcnv( + im = im, ix = ix, km = km, delt = delt, itc = itc, + ntc = ntc, ntk = ntk, ntr = ntr, delp = delp, + prslp = prslp, psp = psp, phil = phil, qtr = qtr, + q1 = q1, t1 = t1, u1 = u1, v1 = v1, + rn = rn, kbot = kbot, ktop = ktop, kcnv = kcnv, + islimsk = islimsk, garea = garea, dot = dot, + ncloud = ncloud, hpbl = hpbl, ud_mf = ud_mf, + dt_mf = dt_mf, cnvw = cnvw, cnvc = cnvc, clam = clam, + c0s = c0s, c1 = c1, pgcon = pgcon, asolfac = asolfac ) + + +def run_model(ncolumns, nrun = 10, compile_gt4py = True): + + ser_count_max = 19 + num_tiles = 6 + + input_0 = read_data(0, True) + + ix = input_0["ix"] + length = DTYPE_INT(ncolumns) + + times_gt4py = np.zeros(nrun) + times_fortran = np.zeros(nrun) + + for i in range(nrun): + + data = read_random_input(length, ix, num_tiles, ser_count_max) + + for key in scalar_vars: + data[key] = input_0[key] + + data["ix"] = length + data["im"] = length + data_gt4py = numpy_dict_to_gt4py_dict(data) + data_fortran = carray2fortranarray(data) + + if i == 0 and compile_gt4py: samfshalcnv_func(data_gt4py) + + # Time GT4Py + tic = time() + samfshalcnv_func(data_gt4py) + toc = time() + times_gt4py[i] = toc - tic + + # Time Fortran + tic = time() + samfshalcnv_fort(data_fortran) + toc = time() + times_fortran[i] = toc - tic + + return times_gt4py, times_fortran + + +if __name__ == "__main__": + + lengths = [32, 128, 512, 2048, 8192, 32768, 131072] #524288] + nrun = 10 + time_mat_gt4py = np.zeros((nrun, len(lengths))) + time_mat_fortran = np.zeros((nrun, len(lengths))) + + print("Compiling fortran code") + f2cmp = "--f2cmap tests/fortran/.f2py_f2cmap" if ISDOCKER else "" + os.system(f"f2py {f2cmp} -c -m shalconv_fortran tests/fortran/samfshalconv_benchmark.f90") + + print(f"Benchmarking samfshalcnv with backend: {BACKEND}") + + for i in range(len(lengths)): + + length = lengths[i] + times_gt4py, times_fortran = run_model(length, nrun, i==0) + time_mat_gt4py[:,i] = times_gt4py + time_mat_fortran[:,i] = times_fortran + + print(f"ix = {length}, Run time: Avg {times_gt4py.mean():.3f}, Std {np.std(times_gt4py):.3e} seconds") + print(f"Fortran run time: Avg {times_fortran.mean():.3e}, Std {np.std(times_fortran):.3e} seconds") + + np.savetxt(f"times-gt4py-{BACKEND}.csv", time_mat_gt4py, delimiter=",") + np.savetxt(f"times-fortran-{BACKEND}.csv", time_mat_fortran, delimiter=",") diff --git a/projects2020/group05/build.sh b/projects2020/group05/build.sh new file mode 100644 index 00000000..613defcc --- /dev/null +++ b/projects2020/group05/build.sh @@ -0,0 +1 @@ +docker build -t hpc4wc_project . diff --git a/projects2020/group05/docs/NOTE.md b/projects2020/group05/docs/NOTE.md new file mode 100644 index 00000000..593ac16c --- /dev/null +++ b/projects2020/group05/docs/NOTE.md @@ -0,0 +1,29 @@ +1. k_index (k_idx) have to be 1-based +2. kbpl -> kpbl +3. no function call in if branch +4. no boolean field and boolean literals +5. gt4py frontend implements `visitor_*` for ast.py +6. get function source by `inspect.getsource` in `GTScriptParser` +7. `visitor_With` -> `_visit_computation_node`, `_visit_inteval_node` +8. usable functions inside gt4py: [`ABS`, `MOD`, `SIN`, `COS`, `TAN`, `ARCSIN`, `ARCCOS`, `ARCTAN`, `SQRT`, `EXP`, `LOG`, + `ISFINITE`, `ISINF`, `ISNAN`, `FLOOR`, `CEIL`, `TRUNC`] +9. can't have temporary var inside if-conditionals +10. clone `serialbox2` from VulcanClimateModeling +11. In the right conda env, build `serialbox2`: `cmake -DCMAKE_INSTALL_PREFIX=/usr/local/serialbox -DSERIALBOX_USE_NETCDF=ON -DSERIALBOX_ENABLE_FORTRAN=ON -DSERIALBOX_TESTING=ON -DSERIALBOX_USE_OPENSSL=OFF ..` +12. best practice for debugging: PyCharm + Docker +13. `dp`, `tem1`, `tem2`, `dv1h`, `rd` ... are not fields +14. [TODO] fix temp vars in part3,4 +15. [ERROR] qtr.shape == (2304, 79, 7), ntr = 2 != qtr.shape[2] + 2 +16. Add `init_kbm_kmax` +17. `heso` not correct -> `qeso` not correct -> should be + ```fortran + qeso(i,k) = 0.01 * fpvsx(to(i,k)) ! fpvs is in pa + qeso(i,k) = eps * qeso(i,k) / (pfld(i,k) + epsm1*qeso(i,k)) + ``` +18. test fpvsx_gt -> pass +19. `fpvs(to);to=t1` -> `fpvs(t1)` +20. delete `fscav` in part3 serialization and delete `delebar` in part4 +21. Solve interval problem for stencil_part34.py line 182 +22. notice argument position! +23. notice bound for forward-backward propagation +24. scalar have to be *keyword argument* in stencils (raise error in x86/cuda backends) \ No newline at end of file diff --git a/projects2020/group05/docs/report.pdf b/projects2020/group05/docs/report.pdf new file mode 100644 index 00000000..df6671ef Binary files /dev/null and b/projects2020/group05/docs/report.pdf differ diff --git a/projects2020/group05/enter.sh b/projects2020/group05/enter.sh new file mode 100644 index 00000000..fc2c0487 --- /dev/null +++ b/projects2020/group05/enter.sh @@ -0,0 +1,5 @@ +docker stop hpc4wc_project 1>/dev/null 2>/dev/null +docker rm hpc4wc_project 1>/dev/null 2>/dev/null +docker run -i -t --rm \ + --mount type=bind,source=`pwd`,target=/work \ + --name=hpc4wc_project hpc4wc_project diff --git a/projects2020/group05/env_daint b/projects2020/group05/env_daint new file mode 100644 index 00000000..4a5a5a7d --- /dev/null +++ b/projects2020/group05/env_daint @@ -0,0 +1,16 @@ +source ~/HPC4WC_venv/bin/activate +module load daint-gpu +module swap PrgEnv-cray PrgEnv-gnu +#module swap gcc gcc/7.3.0 +module swap gcc gcc/8.3.0 +module load cray-netcdf +module load CMake +export SERIALBOX_DIR=/project/c14/install/daint/serialbox2_master/gnu_debug +export PYTHONPATH=${BASEDIR}:${SERIALBOX_DIR}/python:$PYTHONPATH +export NETCDF_LIB=${NETCDF_DIR}/lib +module load Boost +module load cudatoolkit +NVCC_PATH=$(which nvcc) +CUDA_PATH=$(echo $NVCC_PATH | sed -e "s/\/bin\/nvcc//g") +export CUDA_HOME=$CUDA_PATH +export LD_LIBRARY_PATH=$CUDA_PATH/lib64:$LD_LIBRARY_PATH diff --git a/projects2020/group05/get_data.sh b/projects2020/group05/get_data.sh new file mode 100644 index 00000000..af30406e --- /dev/null +++ b/projects2020/group05/get_data.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +# this scripts downloads test data for the physics standalone + +# echo on +set -x + +# get name of standalone package +cwd=`pwd` +dirname=`basename ${cwd}` + +# remove preexisting data directory +test -d ./data +/bin/rm -rf data + +# get data +wget --quiet "ftp://ftp.cscs.ch/in/put/abc/cosmo/fuo/physics_standalone/${dirname}/data.tar.gz" +test -f data.tar.gz || exit 1 +tar -xvf data.tar.gz || exit 1 +/bin/rm -f data.tar.gz 2>/dev/null + +# done diff --git a/projects2020/group05/main.py b/projects2020/group05/main.py new file mode 100644 index 00000000..f6a3a6e5 --- /dev/null +++ b/projects2020/group05/main.py @@ -0,0 +1,41 @@ +from shalconv.serialization import * +from shalconv.samfshalcnv import samfshalcnv_func +from shalconv import * + +if __name__ == "__main__": + + ser_count_max = 19 + num_tiles = 6 + + for tile in range(0, num_tiles): + for ser_count in range(ser_count_max): + + # Read serialized data + print(f"Comparing tile {tile}, ser_count {ser_count}") + input_data = read_data(tile, True, path=DATAPATH, ser_count=ser_count) + output_data = read_data(tile, False, path=DATAPATH, ser_count=ser_count) + + gt4py_dict = numpy_dict_to_gt4py_dict(input_data) + + # Main algorithm + kcnv, kbot, ktop, q1, t1, u1, v1, rn, cnvw, cnvc, ud_mf, dt_mf, qtr = samfshalcnv_func(gt4py_dict) + + # Output data + exp_data = view_gt4pystorage( {"kcnv": kcnv[0,:,0], "kbot": kbot[0,:,0], + "ktop": ktop[0,:,0], "q1": q1[0,:,:], + "t1": t1[0,:,:], "u1": u1[0,:,:], + "v1": v1[0,:,:], "rn": rn[0,:,0], + "cnvw": cnvw[0,:,:], "cnvc": cnvc[0,:,:], + "ud_mf": ud_mf[0,:,:], "dt_mf": dt_mf[0,:,:], + "qtr": qtr[:,:,:]} ) + + # Reference data + ref_data = {"kcnv": output_data["kcnv"], "kbot": output_data["kbot"], + "ktop": output_data["ktop"], "q1": output_data["q1"], + "t1": output_data["t1"], "u1": output_data["u1"], + "v1": output_data["v1"], "rn": output_data["rn"], + "cnvw": output_data["cnvw"], "cnvc": output_data["cnvc"], + "ud_mf": output_data["ud_mf"], "dt_mf": output_data["dt_mf"], + "qtr": output_data["qtr"]} + + compare_data(exp_data, ref_data) diff --git a/projects2020/group05/plot.py b/projects2020/group05/plot.py new file mode 100644 index 00000000..cee2bbf4 --- /dev/null +++ b/projects2020/group05/plot.py @@ -0,0 +1,27 @@ +import matplotlib.pyplot as plt +import numpy as np + +lengths = np.array([32, 128, 512, 2048, 8192, 32768, 131072]) +np_avg = np.array([1.284, 1.415, 2.028, 4.080, 12.102, 52.790, 252.910]) +np_std = np.array([9.604e-02, 7.587e-02, 9.302e-02, 1.172e-01, 2.248e-01, 2.485e-01, 1.105e+00]) +fort_avg = np.array([9.752e-04, 2.578e-03, 1.093e-02, 4.600e-02, 2.102e-01, 8.900e-01, 3.877e+00]) +fort_std = np.array([6.433e-04, 6.065e-05, 1.321e-03, 4.048e-03, 1.797e-02, 5.209e-02, 1.457e-01]) +x86_avg = np.array([0.090, 0.092, 0.103, 0.147, 0.330, 1.216, 5.303]) +x86_std = np.array([7.458e-04, 4.345e-04, 1.491e-03, 5.882e-03, 1.973e-02, 7.084e-02, 6.407e-02]) +cuda_avg = np.array([0.097, 0.147]) +cuda_std = np.array([1.726e-03, 4.610e-02]) +tscale = 2.262/np.sqrt(10) # student-t distirbution double side 95% confidence interval + +capsize=1.0 +plt.figure() +plt.errorbar(lengths, fort_avg, yerr=2*tscale*fort_std, capsize=capsize, label="Original Fortran") +plt.errorbar(lengths, np_avg, yerr=2*tscale*np_std, capsize=capsize, label="Numpy backend") +plt.errorbar(lengths, x86_avg, yerr=2*tscale*x86_std, capsize=capsize, label="x86 backend") +plt.errorbar(lengths[:2], cuda_avg, yerr=2*tscale*cuda_std, capsize=capsize, label="Cuda backend") +plt.xscale("log", basex=2) +plt.yscale("log") +plt.xlabel("Number of columns (ix)") +plt.ylabel("Elapsed time (seconds)") +plt.legend() + +plt.savefig("benchmark.pdf") #dpi=300 diff --git a/projects2020/group05/shalconv/__init__.py b/projects2020/group05/shalconv/__init__.py new file mode 100644 index 00000000..7f853025 --- /dev/null +++ b/projects2020/group05/shalconv/__init__.py @@ -0,0 +1,28 @@ +import numpy as np +from gt4py import gtscript +import os + +ISDOCKER = True +if ISDOCKER: + DATAPATH = "/data" + SERIALBOX_DIR = "/usr/local/serialbox" +else: + DATAPATH = "/scratch/snx3000/course20/physics_standalone/shalconv/data" + SERIALBOX_DIR = "/project/c14/install/daint/serialbox2_master/gnu_debug" +if "GT4PY_BACKEND" in os.environ: + BACKEND = os.environ["GT4PY_BACKEND"] +else: + BACKEND = "numpy"#"numpy"#"gtx86"#debug +REBUILD = False +BACKEND_OPTS = {}#{'verbose': True} if BACKEND.startswith('gt') else {} +default_origin = (0, 0, 0) + +DTYPE_INT = np.int32 +DTYPE_FLOAT = np.float64 + +FIELD_INT = gtscript.Field[DTYPE_INT] +FIELD_FLOAT = gtscript.Field[DTYPE_FLOAT] + +def change_backend(backend): + global BACKEND + BACKEND = backend \ No newline at end of file diff --git a/projects2020/group05/shalconv/funcphys.py b/projects2020/group05/shalconv/funcphys.py new file mode 100644 index 00000000..8c11a4a9 --- /dev/null +++ b/projects2020/group05/shalconv/funcphys.py @@ -0,0 +1,97 @@ +import gt4py as gt +from gt4py import gtscript +import numpy as np +from . import * +from shalconv.physcons import ( + con_ttp, + con_psat, + con_xponal, + con_xponbl, + con_xponai, + con_xponbi +) +from shalconv.kernels.utils import exp + + +### Global variables used in fpvs ### +c1xpvs = None +c2xpvs = None + +### Look-up table for saturation vapor pressure ### +nxpvs = 7501 # Size of look-up table +tbpvs = np.empty(nxpvs) # Look-up table stored as a 1D numpy array + +tbpvs_gt = gt.storage.from_array(tbpvs, BACKEND, (0,), dtype=DTYPE_FLOAT) + + +# Computes saturation vapor pressure table as a function of temperature +# for the table lookup function fpvs +def gpvs(): + global c1xpvs + global c2xpvs + + xmin = 180.0 + xmax = 330.0 + xinc = (xmax - xmin)/(nxpvs - 1) + c2xpvs = 1.0/xinc + c1xpvs = -xmin * c2xpvs + + for jx in range(0, nxpvs): + x = xmin + jx * xinc + tbpvs[jx] = fpvsx(x) + + +# Compute saturation vapor pressure from the temperature. A linear +# interpolation is done between values in a lookup table computed in +# gpvs. +def fpvs(t): + xj = min(max(c1xpvs + c2xpvs * t, 0.0), nxpvs - 1) + jx = int(min(xj, nxpvs - 2)) + + return tbpvs[jx] + (xj - jx) * (tbpvs[jx+1] - tbpvs[jx]) + + +# Compute exact saturation vapor pressure from temperature +def fpvsx(t): + tr = con_ttp/t + tliq = con_ttp + tice = con_ttp - 20.0 + + if t >= tliq: + return con_psat * (tr**con_xponal) * np.exp(con_xponbl * (1.0 - tr)) + elif t < tice: + return con_psat * (tr**con_xponai) * np.exp(con_xponbi * (1.0 - tr)) + else: + w = (t - tice)/(tliq - tice) + pvl = con_psat * (tr**con_xponal) * np.exp(con_xponbl * (1.0 - tr)) + pvi = con_psat * (tr**con_xponai) * np.exp(con_xponbi * (1.0 - tr)) + + return w * pvl + (1.0 - w) * pvi + + +# Function fpvsx as gtscript.function, to be used in stencils +@gtscript.function +def fpvsx_gt(t): + tr = con_ttp/t + tliq = con_ttp + tice = con_ttp - 20.0 + + tmp_l = np.e**(con_xponbl * (1.0 - tr)) + tmp_i = np.e**(con_xponbi * (1.0 - tr)) + ret = 0.0 + w = 0.0 + pvl = 0.0 + pvi = 0.0 + + if t >= tliq: + ret = con_psat * (tr**con_xponal) * tmp_l + elif t < tice: + ret = con_psat * (tr**con_xponai) * tmp_i + else: + w = (t - tice)/(tliq - tice) + pvl = con_psat * (tr**con_xponal) * tmp_l + pvi = con_psat * (tr**con_xponai) * tmp_i + + ret = w * pvl + (1.0 - w) * pvi + + return ret diff --git a/projects2020/group05/shalconv/kernels/__init__.py b/projects2020/group05/shalconv/kernels/__init__.py new file mode 100644 index 00000000..c93b3c44 --- /dev/null +++ b/projects2020/group05/shalconv/kernels/__init__.py @@ -0,0 +1 @@ +from shalconv import * \ No newline at end of file diff --git a/projects2020/group05/shalconv/kernels/stencils_part1.py b/projects2020/group05/shalconv/kernels/stencils_part1.py new file mode 100644 index 00000000..3d66d6b4 --- /dev/null +++ b/projects2020/group05/shalconv/kernels/stencils_part1.py @@ -0,0 +1,314 @@ +import gt4py as gt +from gt4py import gtscript +from gt4py.gtscript import PARALLEL, FORWARD, BACKWARD, computation, interval +from .utils import * +from . import * +from shalconv.funcphys import fpvsx_gt as fpvs +from shalconv.physcons import ( + con_g as g, + con_cp as cp, + con_hvap as hvap, + con_eps as eps, + con_epsm1 as epsm1 +) + +@gtscript.stencil(backend=BACKEND, rebuild=REBUILD) +def pa_to_cb( psp : FIELD_FLOAT, + prslp: FIELD_FLOAT, + delp : FIELD_FLOAT, + ps : FIELD_FLOAT, + prsl : FIELD_FLOAT, + del0 : FIELD_FLOAT ): + + with computation(PARALLEL), interval(...): + + # Convert input Pa terms to Cb terms + ps = psp * 0.001 + prsl = prslp * 0.001 + del0 = delp * 0.001 + + +@gtscript.stencil(backend=BACKEND, rebuild=REBUILD) +def init_col_arr( kcnv : FIELD_INT, + cnvflg: FIELD_INT, + kbot : FIELD_INT, + ktop : FIELD_INT, + kbcon : FIELD_INT, + kb : FIELD_INT, + rn : FIELD_FLOAT, + gdx : FIELD_FLOAT, + garea : FIELD_FLOAT, + *, + km : DTYPE_INT ): + + with computation(PARALLEL), interval(...): + + # Initialize column-integrated and other single-value-per-column + # variable arrays + if (kcnv == 1): + cnvflg = 0 + + if (cnvflg == 1): + kbot = km + 1 + ktop = 0 + + rn = 0.0 + kbcon = km + kb = km + gdx = sqrt(garea) + + +@gtscript.stencil(backend=BACKEND, rebuild=REBUILD) +def init_par_and_arr( islimsk: FIELD_INT, + c0 : FIELD_FLOAT, + t1 : FIELD_FLOAT, + c0t : FIELD_FLOAT, + cnvw : FIELD_FLOAT, + cnvc : FIELD_FLOAT, + ud_mf : FIELD_FLOAT, + dt_mf : FIELD_FLOAT, + *, + c0s : DTYPE_FLOAT, + asolfac: DTYPE_FLOAT, + d0 : DTYPE_FLOAT ): + + with computation(PARALLEL), interval(...): + + # Determine aerosol-aware rain conversion parameter over land + if islimsk == 1: + c0 = c0s * asolfac + else: + c0 = c0s + + # Determine rain conversion parameter above the freezing level + # which exponentially decreases with decreasing temperature + # from Han et al.'s (2017) \cite han_et_al_2017 equation 8 + tem = exp(d0 * (t1 - 273.16)) + if t1 > 273.16: + c0t = c0 + else: + c0t = c0 * tem + + # Initialize convective cloud water and cloud cover to zero + cnvw = 0.0 + cnvc = 0.0 + + # Initialize updraft mass fluxes to zero + ud_mf = 0.0 + dt_mf = 0.0 + +@gtscript.stencil(backend=BACKEND, rebuild=REBUILD, externals={"min": min, "max": max}) +def init_kbm_kmax( kbm : FIELD_INT, + k_idx : FIELD_INT, + kmax : FIELD_INT, + state_buf1: FIELD_INT, + state_buf2: FIELD_INT, + tx1 : FIELD_FLOAT, + ps : FIELD_FLOAT, + prsl : FIELD_FLOAT, + *, + km : DTYPE_INT ): + + with computation(FORWARD): + + # Determine maximum indices for the parcel starting point (kbm) + # and cloud top (kmax) + with interval(0, 1): + tx1 = 1.0/ps + + if prsl * tx1 > 0.7: + kbm = k_idx + 1 + state_buf1 = 1 + else: + kbm = km + state_buf1 = 0 # means kbm is set to default `km` + + with interval(1, None): + tx1 = 1.0 / ps + + if prsl * tx1 > 0.7: + kbm = k_idx + 1 + state_buf1 = 1 + elif state_buf1[0,0,-1]: + kbm = kbm[0,0,-1] + state_buf1 = 1 + else: + kbm = km + state_buf1 = 0 + + with computation(FORWARD): + + with interval(0, 1): + if prsl * tx1 > 0.6: + kmax = k_idx + 1 + state_buf2 = 1 # reuse flg + else: + kmax = km + state_buf2 = 0 # means kmax is set to default `km` + + with interval(1, None): + if prsl * tx1 > 0.6: + kmax = k_idx + 1 + state_buf2 = 1 + elif state_buf2[0,0,-1]: + kmax = kmax[0,0,-1] + state_buf2 = 1 + else: + kmax = km + state_buf2 = 0 + + with computation(BACKWARD): + + with interval(-1, None): + kbm = min(kbm, kmax) + + with interval(0, -1): + kbm = kbm[0,0,1] + kmax = kmax[0,0,1] + kbm = min(kbm, kmax) + +@gtscript.stencil(backend=BACKEND, rebuild=REBUILD, externals={"min": min, "max": max, "fpvs": fpvs}) +def init_final( kbm : FIELD_INT, + k_idx : FIELD_INT, + kmax : FIELD_INT, + flg : FIELD_INT, + cnvflg: FIELD_INT, + kpbl : FIELD_INT, + tx1 : FIELD_FLOAT, + ps : FIELD_FLOAT, + prsl : FIELD_FLOAT, + zo : FIELD_FLOAT, + phil : FIELD_FLOAT, + zi : FIELD_FLOAT, + pfld : FIELD_FLOAT, + eta : FIELD_FLOAT, + hcko : FIELD_FLOAT, + qcko : FIELD_FLOAT, + qrcko : FIELD_FLOAT, + ucko : FIELD_FLOAT, + vcko : FIELD_FLOAT, + dbyo : FIELD_FLOAT, + pwo : FIELD_FLOAT, + dellal: FIELD_FLOAT, + to : FIELD_FLOAT, + qo : FIELD_FLOAT, + uo : FIELD_FLOAT, + vo : FIELD_FLOAT, + wu2 : FIELD_FLOAT, + buo : FIELD_FLOAT, + drag : FIELD_FLOAT, + cnvwt : FIELD_FLOAT, + qeso : FIELD_FLOAT, + heo : FIELD_FLOAT, + heso : FIELD_FLOAT, + hpbl : FIELD_FLOAT, + t1 : FIELD_FLOAT, + q1 : FIELD_FLOAT, + u1 : FIELD_FLOAT, + v1 : FIELD_FLOAT, + *, + km : DTYPE_INT ): + + with computation(PARALLEL), interval(...): + + # Calculate hydrostatic height at layer centers assuming a flat + # surface (no terrain) from the geopotential + zo = phil/g + + # Initialize flg in parallel computation block + flg = cnvflg + + kpbl = 1 + + with computation(PARALLEL), interval(0, -1): + + # Calculate interface height + zi = 0.5 * (zo[0, 0, 0] + zo[0, 0, +1]) + + with computation(FORWARD),interval(1,-1): + + # Find the index for the PBL top using the PBL height; enforce + # that it is lower than the maximum parcel starting level + flg = flg[0, 0, -1] + kpbl = kpbl[0, 0, -1] + if (flg == 1) and (zo <= hpbl): + kpbl = k_idx + else: + flg = 0 # False + + with computation(FORWARD), interval(-1, None): + flg = flg[0, 0, -1] + kpbl = kpbl[0, 0, -1] + + with computation(BACKWARD),interval(0,-1): + + # Propagate results back to update whole field + kpbl = kpbl[0, 0, 1] + flg = flg[0, 0, 1] + + with computation(PARALLEL), interval(...): + + kpbl = min(kpbl, kbm) + + # Temporary var have to be defined outside of if-clause + val1 = 0.0 + val2 = 0.0 + tem = 0.0 + fpvsto = fpvs(t1) #fpvs(to) and to = t1 + + if cnvflg and k_idx <= kmax: + + # Convert prsl from centibar to millibar, set normalized mass + # flux to 1, cloud properties to 0, and save model state + # variables (after advection/turbulence) + pfld = prsl * 10.0 + eta = 1.0 + hcko = 0.0 + qcko = 0.0 + qrcko = 0.0 + ucko = 0.0 + vcko = 0.0 + dbyo = 0.0 + pwo = 0.0 + dellal = 0.0 + to = t1 + qo = q1 + uo = u1 + vo = v1 + wu2 = 0.0 + buo = 0.0 + drag = 0.0 + cnvwt = 0.0 + + # Calculate saturation specific humidity and enforce minimum + # moisture values + qeso = 0.01 * fpvsto + qeso = (eps * qeso)/(pfld + epsm1 * qeso) # fpsv is a function (can't be called inside conditional), also how to access lookup table? + val1 = 1.0e-8 + val2 = 1.0e-10 + qeso = max(qeso, val1 ) + qo = max(qo, val2) + + # Calculate moist static energy (heo) and saturation moist + # static energy (heso) + tem = phil + cp * to + heo = tem + hvap * qo + heso = tem + hvap * qeso + + +@gtscript.stencil(backend=BACKEND, rebuild=REBUILD) +def init_tracers( cnvflg: FIELD_INT, + k_idx : FIELD_INT, + kmax : FIELD_INT, + ctr : FIELD_FLOAT, + ctro : FIELD_FLOAT, + ecko : FIELD_FLOAT, + qtr : FIELD_FLOAT ): + + with computation(PARALLEL), interval(...): + + # Initialize tracer variables + if cnvflg == 1 and k_idx <= kmax: + ctr = qtr + ctro = qtr + ecko = 0.0 diff --git a/projects2020/group05/shalconv/kernels/stencils_part2.py b/projects2020/group05/shalconv/kernels/stencils_part2.py new file mode 100644 index 00000000..1ebc1863 --- /dev/null +++ b/projects2020/group05/shalconv/kernels/stencils_part2.py @@ -0,0 +1,1127 @@ +import gt4py as gt +from gt4py import gtscript +from gt4py.gtscript import PARALLEL, FORWARD, BACKWARD, computation, interval +from shalconv.funcphys import fpvsx_gt as fpvs +from . import * + +from shalconv.physcons import ( + con_g as g, + con_cp as cp, + con_hvap as hvap, + con_rv as rv, + con_fvirt as fv, + con_t0c as t0c, + con_rd as rd, + con_cvap as cvap, + con_cliq as cliq, + con_eps as eps, + con_epsm1 as epsm1 +) + +elocp = hvap/cp +el2orc = hvap * hvap/(rv * cp) +d0 = 0.001 +cm = 1.0 +delta = fv +fact1 = (cvap - cliq)/rv +fact2 = hvap/rv - fact1 * t0c +clamd = 0.1 +tkemx = 0.65 +tkemn = 0.05 +dtke = tkemx - tkemn +dthk = 25.0 +cinpcrmx = 180.0 +cinpcrmn = 120.0 +cinacrmx = -120.0 +cinacrmn = -80.0 +crtlamd = 3.0e-4 +dtmax = 10800.0 +dtmin = 600.0 +bet1 = 1.875 +cd1 = 0.506 +f1 = 2.0 +gam1 = 0.5 +betaw = 0.03 +dxcrt = 15.0e3 +h1 = 0.33333333 +tf = 233.16 +tcr = 263.16 +tcrf = 1.0/(tcr - tf) + +aafac = 0.05 +evfact = 0.3 +evfactl = 0.3 +w1l = -8.0e-3 +w2l = -4.0e-2 +w3l = -5.0e-3 +w4l = -5.0e-4 +w1s = -2.0e-4 +w2s = -2.0e-3 +w3s = -1.0e-3 +w4s = -2.0e-5 + +externals = { + "fpvs":fpvs +} + + +@gtscript.stencil(backend=BACKEND,externals=externals, rebuild=REBUILD) +def stencil_static0( + cnvflg: FIELD_INT, + hmax : FIELD_FLOAT, + heo : FIELD_FLOAT, + kb : FIELD_INT, + k_idx : FIELD_INT, + kpbl : FIELD_INT, + kmax : FIELD_INT, + zo : FIELD_FLOAT, + to : FIELD_FLOAT, + qeso : FIELD_FLOAT, + qo : FIELD_FLOAT, + po : FIELD_FLOAT, + uo : FIELD_FLOAT, + vo : FIELD_FLOAT, + heso : FIELD_FLOAT, + pfld : FIELD_FLOAT ): + + """ + Scale-Aware Mass-Flux Shallow Convection + :to use the k_idx[1,0:im,0:km] as storage of 1 to k_idx index. + """ + with computation(FORWARD), interval(0,1): + if cnvflg == 1: + hmax = heo + kb = 1 + + with computation(FORWARD), interval(1,None): + hmax = hmax[0,0,-1] + kb = kb[0,0,-1] + if (cnvflg == 1) and (k_idx <= kpbl): + if(heo > hmax): + kb = k_idx + hmax = heo + + # To make all slice like the final slice + with computation(BACKWARD), interval(0,-1): + kb = kb[0,0,1] + hmax = hmax[0,0,1] + + with computation(FORWARD), interval(0,-1): + + tmp = fpvs(to[0,0,1]) + dz = 1. + dp = 1. + es = 1. + pprime = 1. + qs = 1. + dqsdp = 1. + desdt = 1. + dqsdt = 1. + gamma = 1. + dt = 1. + dq = 1. + + if (cnvflg[0,0,0] and k_idx[0,0,0] <= kmax[0,0,0]-1): + dz = .5 * (zo[0,0,1] - zo[0,0,0]) + dp = .5 * (pfld[0,0,1] - pfld[0,0,0]) + es = 0.01 * tmp # fpvs is in pa + pprime = pfld[0,0,1] + epsm1 * es + qs = eps * es / pprime + dqsdp = - qs / pprime + desdt = es * (fact1 / to[0,0,1] + fact2 / (to[0,0,1]**2)) + dqsdt = qs * pfld[0,0,1] * desdt / (es * pprime) + gamma = el2orc * qeso[0,0,1] / (to[0,0,1]**2) + dt = (g * dz + hvap * dqsdp * dp) / (cp * (1. + gamma)) + dq = dqsdt * dt + dqsdp * dp + to = to[0,0,1] + dt + qo = qo[0,0,1] + dq + po = .5 * (pfld[0,0,0] + pfld[0,0,1]) + + with computation(FORWARD), interval(0,-1): + + tmp = fpvs(to) + + if (cnvflg[0,0,0] and k_idx[0,0,0] <= kmax[0,0,0]-1): + qeso = 0.01 * tmp # fpvs is in pa + qeso = eps * qeso[0,0,0] / (po[0,0,0] + epsm1*qeso[0,0,0]) + #val1 = 1.e-8 + qeso = qeso[0,0,0] if (qeso[0,0,0]>1.e-8) else 1.e-8 + #val2 = 1.e-10 + qo = qo[0,0,0] if (qo[0,0,0]>1.e-10) else 1.e-10 + #qo = min(qo[0,0,0],qeso[0,0,0]) + heo = .5 * g * (zo[0,0,0] + zo[0,0,1]) + \ + cp * to[0,0,0] + hvap * qo[0,0,0] + heso = .5 * g * (zo[0,0,0] + zo[0,0,1]) + \ + cp * to[0,0,0] + hvap * qeso[0,0,0] + uo = .5 * (uo[0,0,0] + uo[0,0,1]) + vo = .5 * (vo[0,0,0] + vo[0,0,1]) + + +## ntr stencil put at last +@gtscript.stencil(backend=BACKEND,externals=externals, rebuild=REBUILD, **BACKEND_OPTS) +def stencil_ntrstatic0( + cnvflg: FIELD_INT, + k_idx : FIELD_INT, + kmax : FIELD_INT, + ctro : FIELD_FLOAT ): + + with computation(PARALLEL), interval(0,-1): + + if (cnvflg == 1 ) and (k_idx <= (kmax-1)): + ctro = .5 * (ctro + ctro[0,0,1]) + + +@gtscript.stencil(backend=BACKEND, rebuild=REBUILD) +def stencil_static1( + cnvflg: FIELD_INT, + flg : FIELD_INT, + kbcon : FIELD_INT, + kmax : FIELD_INT, + k_idx : FIELD_INT, + kbm : FIELD_INT, + kb : FIELD_INT, + heo_kb: FIELD_FLOAT, + heso : FIELD_FLOAT ): + + with computation(PARALLEL), interval(...): + flg = cnvflg + if(flg): + kbcon = kmax + + with computation(FORWARD), interval(1,-1): + kbcon = kbcon[0,0,-1] + flg = flg[0,0,-1] + if (flg and k_idx < kbm): + # To use heo_kb to represent heo(i,kb(i)) + if(k_idx[0,0,0] > kb[0,0,0] and heo_kb > heso[0,0,0]): + kbcon = k_idx + flg = 0 + + # To make all slices like the final slice + with computation(FORWARD), interval(-1,None): + kbcon = kbcon[0,0,-1] + flg = flg[0,0,-1] + + with computation(BACKWARD), interval(0,-1): + kbcon = kbcon[0,0,1] + flg = flg[0,0,1] + + with computation(PARALLEL), interval(...): + if(cnvflg): + if(kbcon == kmax): + cnvflg = 0 + + +## Judge LFC and return 553-558 +@gtscript.stencil(backend=BACKEND, rebuild=REBUILD) +def stencil_static2( + cnvflg : FIELD_INT, + pdot : FIELD_FLOAT, + dot_kbcon : FIELD_FLOAT, + islimsk : FIELD_INT, + k_idx : FIELD_INT, + kbcon : FIELD_INT, + kb : FIELD_INT, + pfld_kb : FIELD_FLOAT, + pfld_kbcon: FIELD_FLOAT ): + + with computation(PARALLEL), interval(...): + if(cnvflg): + # To use dotkbcon to represent dot(i,kbcon(i)) + #pdot(i) = 10.* dotkbcon + pdot[0,0,0] = 0.01 * dot_kbcon # Now dot is in Pa/s + + with computation(PARALLEL), interval(...): + w1 = w1s + w2 = w2s + w3 = w3s + w4 = w4s + tem = 0. + tem1 = 0. + ptem = 0. + ptem1 = 0. + cinpcr = 0. + + if(cnvflg): + if(islimsk == 1): + w1 = w1l + w2 = w2l + w3 = w3l + w4 = w4l + if(pdot <= w4): + tem = (pdot - w4) / (w3 - w4) + elif(pdot >= -w4): + tem = - (pdot + w4) / (w4 - w3) + else: + tem = 0. + + tem = tem if (tem>-1) else -1 + tem = tem if (tem<1) else 1 + ptem = 1. - tem + ptem1 = .5*(cinpcrmx-cinpcrmn) + cinpcr = cinpcrmx - ptem * ptem1 + + # To use pfld_kb and pfld_kbcon to represent pfld(i,kb(i)) + tem1 = pfld_kb - pfld_kbcon + if(tem1 > cinpcr): + cnvflg = 0 + + +## Do totflg judgement and return +## if ntk > 0 : also need to define ntk dimension to 1 +@gtscript.stencil(backend=BACKEND, rebuild=REBUILD) +def stencil_static3( + sumx : FIELD_FLOAT, + tkemean: FIELD_FLOAT, + cnvflg : FIELD_INT, + k_idx : FIELD_INT, + kb : FIELD_INT, + kbcon : FIELD_INT, + zo : FIELD_FLOAT, + qtr : FIELD_FLOAT, + clamt : FIELD_FLOAT, + *, + clam : DTYPE_FLOAT ): + + with computation(BACKWARD), interval(-1, None): + if cnvflg == 1: + sumx = 0. + tkemean = 0. + + with computation(BACKWARD), interval(0, -1): + dz = 0. + tem = 0. + tkemean = tkemean[0, 0, 1] + sumx = sumx[0, 0, 1] + + if(cnvflg): + if(k_idx >= kb) and (k_idx < kbcon): + dz = zo[0,0,1] - zo[0,0,0] + tem = 0.5 * (qtr[0,0,0]+qtr[0,0,1]) + tkemean = tkemean[0,0,1] + tem * dz #dz, tem to be 3d + sumx = sumx[0,0,1] + dz + + with computation(FORWARD), interval(1, None): + tkemean = tkemean[0,0,-1] + sumx = sumx[0,0,-1] + + with computation(PARALLEL), interval(...): + tkemean = tkemean / sumx + tem1 = 1. - 2. * (tkemx - tkemean) / dtke + + if cnvflg: + if tkemean > tkemx: # tkemx, clam, clamd, tkemnm, dtke to be 3d + clamt = clam + clamd + elif tkemean < tkemn: + clamt = clam - clamd + else: + clamt = clam + clamd * tem1 + + +## else : +@gtscript.stencil(backend=BACKEND, rebuild=REBUILD) +def stencil_static4( + cnvflg: FIELD_INT, + clamt : FIELD_FLOAT, + *, + clam : DTYPE_FLOAT ): + + with computation(PARALLEL), interval(...): + if(cnvflg): + clamt = clam + + +## Start updraft entrainment rate. +## pass +@gtscript.stencil(backend=BACKEND, rebuild=REBUILD) +def stencil_static5( + cnvflg: FIELD_INT, + xlamue: FIELD_FLOAT, + clamt : FIELD_FLOAT, + zi : FIELD_FLOAT, + xlamud: FIELD_FLOAT, + k_idx : FIELD_INT, + kbcon : FIELD_INT, + kb : FIELD_INT, + #dz : FIELD_FLOAT, + #ptem : FIELD_FLOAT, + eta : FIELD_FLOAT, + ktconn: FIELD_INT, + kmax : FIELD_INT, + kbm : FIELD_INT, + hcko : FIELD_FLOAT, + ucko : FIELD_FLOAT, + vcko : FIELD_FLOAT, + heo : FIELD_FLOAT, + uo : FIELD_FLOAT, + vo : FIELD_FLOAT ): + + with computation(FORWARD), interval(0,-1): + if(cnvflg): + xlamue = clamt / zi + + with computation(BACKWARD), interval(-1,None): + if(cnvflg): + xlamue[0,0,0] = xlamue[0,0,-1] + + with computation(PARALLEL), interval(...): + if(cnvflg): + #xlamud(i) = xlamue(i,kbcon(i)) + #xlamud(i) = crtlamd + xlamud = 0.001 * clamt + + with computation(BACKWARD), interval(0,-1): + dz = 0. + ptem = 0. + if (cnvflg): + if( k_idx < kbcon and k_idx >= kb): + dz = zi[0,0,1] - zi[0,0,0] + ptem = 0.5*(xlamue[0,0,0]+xlamue[0,0,1])-xlamud[0,0,0] + eta = eta[0,0,1] / (1. + ptem * dz) + + with computation(PARALLEL), interval(...): + flg = cnvflg + + with computation(FORWARD), interval(1,-1): + flg = flg[0,0,-1] + kmax = kmax[0,0,-1] + ktconn = ktconn[0,0,-1] + kbm = kbm[0,0,-1] + if(flg): + if(k_idx > kbcon and k_idx < kmax): + dz = zi[0,0,0] - zi[0,0,-1] + ptem = 0.5*(xlamue[0,0,0]+xlamue[0,0,-1])-xlamud[0,0,0] + eta = eta[0,0,-1] * (1 + ptem * dz) + + if(eta <= 0.): + kmax = k_idx + ktconn = k_idx + kbm = kbm if (kbm [1,i,k_idx] +## pass +@gtscript.stencil(backend=BACKEND, rebuild=REBUILD) +def stencil_ntrstatic1( + cnvflg: FIELD_INT, + k_idx : FIELD_INT, + kb : FIELD_INT, + ecko : FIELD_FLOAT, + ctro : FIELD_FLOAT ): + + with computation(PARALLEL), interval(...): + if (cnvflg == 1) and (k_idx == kb): + ecko = ctro + + +## Line 769 +## Calculate the cloud properties as a parcel ascends, modified by entrainment and detrainment. Discretization follows Appendix B of Grell (1993) \cite grell_1993 . Following Han and Pan (2006) \cite han_and_pan_2006, the convective momentum transport is reduced by the convection-induced pressure gradient force by the constant "pgcon", currently set to 0.55 after Zhang and Wu (2003) \cite zhang_and_wu_2003 . +## pass +@gtscript.stencil(backend=BACKEND, rebuild=REBUILD) +def stencil_static7( + cnvflg: FIELD_INT, + k_idx : FIELD_INT, + kb : FIELD_INT, + kmax : FIELD_INT, + zi : FIELD_FLOAT, + xlamue: FIELD_FLOAT, + xlamud: FIELD_FLOAT, + hcko : FIELD_FLOAT, + heo : FIELD_FLOAT, + dbyo : FIELD_FLOAT, + heso : FIELD_FLOAT, + ucko : FIELD_FLOAT, + uo : FIELD_FLOAT, + vcko : FIELD_FLOAT, + vo : FIELD_FLOAT, + *, + pgcon : DTYPE_FLOAT ): + + with computation(FORWARD), interval(1,-1): + dz = 0. + tem = 0. + tem1 = 0. + ptem = 0. + ptem1 = 0. + factor = 0. + + if(cnvflg): + if(k_idx > kb and k_idx < kmax): + dz = zi[0,0,0] - zi[0,0,-1] + tem = 0.5 * (xlamue[0,0,0] + xlamue[0,0,-1]) * dz + tem1 = 0.5 * xlamud * dz + factor = 1. + tem - tem1 + hcko = ( (1. - tem1) * hcko[0,0,-1] + tem * 0.5 * (heo + heo[0,0,-1]) )/factor + dbyo = hcko - heso + + tem = 0.5 * cm * tem + factor = 1. + tem + ptem = tem + pgcon + ptem1 = tem - pgcon + ucko = ( (1. - tem) * ucko[0,0,-1] + ptem * uo + ptem1 * uo[0,0,-1] )/factor + vcko = ( (1. - tem) * vcko[0,0,-1] + ptem * vo + ptem1 * vo[0,0,-1] )/factor + + +## for n = 1, ntr: +## pass +@gtscript.stencil(backend=BACKEND, rebuild=REBUILD) +def stencil_ntrstatic2( + cnvflg: FIELD_INT, + k_idx : FIELD_INT, + kb : FIELD_INT, + kmax : FIELD_INT, + zi : FIELD_FLOAT, + xlamue: FIELD_FLOAT, + ecko : FIELD_FLOAT, + ctro : FIELD_FLOAT ): + + with computation(FORWARD), interval(1,-1): + tem = 0.0 + dz = 0.0 + factor = 0.0 + + if (cnvflg): + if(k_idx > kb and k_idx < kmax): + dz = zi - zi[0,0,-1] + tem = 0.25 * (xlamue+xlamue[0,0,-1]) * dz + factor = 1. + tem + ecko = ((1.-tem)*ecko[0,0,-1]+tem*(ctro+ctro[0,0,-1]))/factor + + +## enddo +@gtscript.stencil(backend=BACKEND, rebuild=REBUILD) +def stencil_update_kbcon1_cnvflg( + dbyo : FIELD_FLOAT, + cnvflg: FIELD_INT, + kmax : FIELD_INT, + kbm : FIELD_INT, + kbcon : FIELD_INT, + kbcon1: FIELD_INT, + flg : FIELD_INT, + k_idx : FIELD_INT ): + + with computation(FORWARD), interval(0, 1): + flg = cnvflg + kbcon1 = kmax + + with computation(FORWARD), interval(1, None): + flg = flg[0, 0, -1] + kbcon1 = kbcon1[0, 0, -1] + + if (flg == 1) and (k_idx < kbm): + if (k_idx >= kbcon) and (dbyo > 0.): + kbcon1 = k_idx + flg = 0 + + with computation(BACKWARD), interval(0, -1): + flg = flg[0, 0, 1] + kbcon1 = kbcon1[0, 0, 1] + + with computation(PARALLEL), interval(...): + if (cnvflg): + if (kbcon1 == kmax): + cnvflg = 0 + + +## pass +@gtscript.stencil(backend=BACKEND, rebuild=REBUILD) +def stencil_static9( + cnvflg : FIELD_INT, + pfld_kbcon : FIELD_FLOAT, + pfld_kbcon1: FIELD_FLOAT ): + + with computation(PARALLEL),interval(...): + tem = 0. + + if(cnvflg): + + # Use pfld_kbcon and pfld_kbcon1 to represent + #tem = pfld(i,kbcon(i)) - pfld(i,kbcon1(i)) + tem = pfld_kbcon - pfld_kbcon1 + if(tem > dthk): + cnvflg = 0 + + +## Judge totflg return + +## Calculate convective inhibition +## pass +@gtscript.stencil(backend=BACKEND, rebuild=REBUILD) +def stencil_static10( + cina : FIELD_FLOAT, + cnvflg : FIELD_INT, + k_idx : FIELD_INT, + kb : FIELD_INT, + kbcon1 : FIELD_INT, + zo : FIELD_FLOAT, + qeso : FIELD_FLOAT, + to : FIELD_FLOAT, + dbyo : FIELD_FLOAT, + qo : FIELD_FLOAT, + pdot : FIELD_FLOAT, + islimsk: FIELD_INT ): + + with computation(FORWARD), interval(1,-1): + dz1 = 0. + gamma = 0. + rfact = 0. + cina = cina[0,0,-1] + + if (cnvflg): + if(k_idx > kb and k_idx < kbcon1): + dz1 = zo[0,0,1] - zo + gamma = el2orc * qeso / (to*to) + rfact = 1. + delta * cp * gamma * to / hvap + cina = cina + dz1 * (g / (cp * to)) * \ + dbyo / (1. + gamma) * rfact + #val = 0. + cina = (cina + + #dz1 * eta(i,k_idx) * g * delta * + dz1 * g * delta * + (qeso - qo)) if ((qeso - qo)>0.) else cina + + # To make all slices like the final slice + with computation(FORWARD), interval(-1,None): + cina = cina[0,0,-1] + + with computation(BACKWARD), interval(0,-1): + cina = cina[0,0,1] + + with computation(PARALLEL), interval(...): + w1 = w1s + w2 = w2s + w3 = w3s + w4 = w4s + tem = 0. + tem1 = 0. + cinacr = 0. + + if(cnvflg): + if(islimsk == 1): + w1 = w1l + w2 = w2l + w3 = w3l + w4 = w4l + + if(pdot <= w4): + tem = (pdot - w4) / (w3 - w4) + elif(pdot >= -w4): + tem = - (pdot + w4) / (w4 - w3) + else: + tem = 0. + + #val1 = -1. + tem = tem if (tem > -1.) else -1. + #val2 = 1. + tem = tem if (tem < 1.) else 1. + tem = 1. - tem + tem1 = .5*(cinacrmx-cinacrmn) + cinacr = cinacrmx - tem * tem1 + #cinacr = cinacrmx + if(cina < cinacr): + cnvflg = 0 + + +## totflag and return + +## Determine first guess cloud top as the level of zero buoyancy +## limited to the level of P/Ps=0.7 +## pass +@gtscript.stencil(backend=BACKEND,rebuild=REBUILD) +def stencil_static11( + flg : FIELD_INT, + cnvflg: FIELD_INT, + ktcon : FIELD_INT, + kbm : FIELD_INT, + kbcon1: FIELD_INT, + dbyo : FIELD_FLOAT, + kbcon : FIELD_INT, + del0 : FIELD_FLOAT, + xmbmax: FIELD_FLOAT, + aa1 : FIELD_FLOAT, + kb : FIELD_INT, + qcko : FIELD_FLOAT, + qo : FIELD_FLOAT, + qrcko : FIELD_FLOAT, + zi : FIELD_FLOAT, + qeso : FIELD_FLOAT, + to : FIELD_FLOAT, + xlamue: FIELD_FLOAT, + xlamud: FIELD_FLOAT, + eta : FIELD_FLOAT, + c0t : FIELD_FLOAT, + dellal: FIELD_FLOAT, + buo : FIELD_FLOAT, + drag : FIELD_FLOAT, + zo : FIELD_FLOAT, + k_idx : FIELD_INT, + pwo : FIELD_FLOAT, + cnvwt : FIELD_FLOAT, + *, + c1 : DTYPE_FLOAT, + dt2 : DTYPE_FLOAT, + ncloud: DTYPE_INT ): + + with computation(PARALLEL), interval(...): + flg = cnvflg + if(flg): + ktcon = kbm + + with computation(FORWARD), interval(1,-1): + flg = flg[0,0,-1] + ktcon = ktcon[0,0,-1] + if (flg and k_idx < kbm): + if(k_idx > kbcon1 and dbyo < 0.): + ktcon = k_idx + flg = 0 + + # To make all slices like final slice + with computation(FORWARD), interval(-1,None): + flg = flg[0,0,-1] + ktcon = ktcon[0,0,-1] + + with computation(BACKWARD), interval(0,-1): + flg = flg[0,0,1] + ktcon = ktcon[0,0,1] + + + # Specify upper limit of mass flux at cloud base + + with computation(FORWARD), interval(...): + dp = 0. + + if(k_idx != 1): + xmbmax = xmbmax[0,0,-1] + + if(cnvflg): + if(k_idx == kbcon): + dp = 1000. * del0 + + xmbmax = dp / (2. * g * dt2) + + with computation(BACKWARD), interval(0,-1): + xmbmax = xmbmax[0,0,1] + + # Compute cloud moisture property and precipitation + with computation(PARALLEL), interval(...): + if (cnvflg): + aa1 = 0. + if (k_idx == kb): + qcko = qo + qrcko = qo + + # Calculate the moisture content of the entraining/detraining parcel (qcko) and the value it would have if just saturated (qrch), according to equation A.14 in Grell (1993) \cite grell_1993 . Their difference is the amount of convective cloud water (qlk = rain + condensate). Determine the portion of convective cloud water that remains suspended and the portion that is converted into convective precipitation (pwo). Calculate and save the negative cloud work function (aa1) due to water loading. Above the level of minimum moist static energy, some of the cloud water is detrained into the grid-scale cloud water from every cloud layer with a rate of 0.0005 \f$m^{-1}\f$ (dellal). + with computation(FORWARD), interval(1,-1): + dz = 0. + gamma = 0. + qrch = 0. + tem = 0. + tem1 = 0. + factor = 0. + dq = 0. + etah = 0. + dp = 0. + ptem = 0. + qlk = 0. + rfact = 0. + + if (cnvflg): + if(k_idx > kb and k_idx < ktcon): + dz = zi - zi[0,0,-1] + gamma = el2orc * qeso / (to**2) + qrch = qeso \ + + gamma * dbyo / (hvap * (1. + gamma)) + #j + tem = 0.5 * (xlamue+xlamue[0,0,-1]) * dz + tem1 = 0.5 * xlamud * dz + factor = 1. + tem - tem1 + qcko = ((1.-tem1)*qcko[0,0,-1]+tem*0.5* + (qo+qo[0,0,-1]))/factor + qrcko = qcko + #j + dq = eta * (qcko - qrch) + + # rhbar(i) = rhbar(i) + qo(i,k_idx) / qeso(i,k_idx) + + # Below lfc check if there is excess moisture to release + # latent heat + if(k_idx >= kbcon and dq > 0.): + etah = .5 * (eta + eta[0,0,-1]) + dp = 1000. * del0 + + if(ncloud > 0): + ptem = c0t + c1 + qlk = dq / (eta + etah * ptem * dz) + dellal = etah * c1 * dz * qlk * g / dp + else: + qlk = dq / (eta + etah * c0t * dz) + + buo = buo - g * qlk + qcko = qlk + qrch + pwo = etah * c0t * dz * qlk + cnvwt = etah * qlk * g / dp + + if(k_idx >= kbcon): + rfact = 1. + delta * cp * gamma \ + * to / hvap + buo = buo + (g / (cp * to)) \ + * dbyo / (1. + gamma) \ + * rfact + + #val = 0. + buo = (buo + g * delta * (qeso - qo)) if((qeso - qo)>0.) else buo + drag = xlamue if(xlamue > xlamud) else xlamud + + # L1064: Calculate the cloud work function according to Pan and Wu (1995) \cite pan_and_wu_1995 equation 4 + with computation(PARALLEL), interval(...): + if (cnvflg): + aa1 = 0. + + with computation(FORWARD), interval(1,-1): + aa1 = aa1[0,0,-1] + dz1 = 0. + if (cnvflg): + if(k_idx >= kbcon and k_idx < ktcon): + dz1 = zo[0,0,1] - zo + aa1 = aa1 + buo * dz1 + + # To make all slices like final slice + with computation(FORWARD), interval(-1,None): + aa1 = aa1[0,0,-1] + with computation(BACKWARD), interval(0,-1): + aa1 = aa1[0,0,1] + + with computation(PARALLEL), interval(...): + if(cnvflg and aa1 <= 0.): + cnvflg = 0 + + +## totflg and return + +## Estimate the onvective overshooting as the level +## where the [aafac * cloud work function] becomes zero, +## which is the final cloud top +## limited to the level of P/Ps=0.7 + +## Continue calculating the cloud work function past the point of neutral buoyancy to represent overshooting according to Han and Pan (2011) \cite han_and_pan_2011 . Convective overshooting stops when \f$ cA_u < 0\f$ where \f$c\f$ is currently 10%, or when 10% of the updraft cloud work function has been consumed by the stable buoyancy force. Overshooting is also limited to the level where \f$p=0.7p_{sfc}\f$. +## pass +@gtscript.stencil(backend=BACKEND, rebuild=REBUILD) +def stencil_static12( + cnvflg: FIELD_INT, + aa1 : FIELD_FLOAT, + flg : FIELD_INT, + ktcon1: FIELD_INT, + kbm : FIELD_INT, + k_idx : FIELD_INT, + ktcon : FIELD_INT, + zo : FIELD_FLOAT, + qeso : FIELD_FLOAT, + to : FIELD_FLOAT, + dbyo : FIELD_FLOAT, + zi : FIELD_FLOAT, + xlamue: FIELD_FLOAT, + xlamud: FIELD_FLOAT, + qcko : FIELD_FLOAT, + qrcko : FIELD_FLOAT, + qo : FIELD_FLOAT, + eta : FIELD_FLOAT, + del0 : FIELD_FLOAT, + c0t : FIELD_FLOAT, + pwo : FIELD_FLOAT, + cnvwt : FIELD_FLOAT, + buo : FIELD_FLOAT, + wu2 : FIELD_FLOAT, + wc : FIELD_FLOAT, + sumx : FIELD_FLOAT, + kbcon1: FIELD_INT, + drag : FIELD_FLOAT, + dellal: FIELD_FLOAT, + *, + c1 : DTYPE_FLOAT, + ncloud: DTYPE_INT ): + + with computation(PARALLEL), interval(...): + if (cnvflg): + aa1 = aafac * aa1 + + flg = cnvflg + ktcon1 = kbm + + with computation(FORWARD), interval(1,-1): + dz1 = 0. + gamma = 0. + rfact = 0. + aa1 = aa1[0,0,-1] + ktcon1 = ktcon1[0,0,-1] + flg = flg[0,0,-1] + + if (flg): + if(k_idx >= ktcon and k_idx < kbm): + dz1 = zo[0,0,1] - zo + gamma = el2orc * qeso / (to**2) + rfact = 1. + delta * cp * gamma \ + * to / hvap + aa1 = aa1 + \ + dz1 * (g / (cp * to)) \ + * dbyo / (1. + gamma) \ + * rfact + + #val = 0. + #aa1(i) = aa1(i) + + # dz1 * eta(i,k_idx) * g * delta * + # dz1 * g * delta * + # max(val,(qeso(i,k_idx) - qo(i,k_idx))) + + if(aa1 < 0.): + ktcon1 = k_idx + flg = 0 + + # To make all slice like final slice + with computation(FORWARD), interval(-1,None): + aa1 = aa1[0,0,-1] + ktcon1 = ktcon1[0,0,-1] + flg = flg[0,0,-1] + + with computation(BACKWARD), interval(0,-1): + aa1 = aa1[0,0,1] + ktcon1 = ktcon1[0,0,1] + flg = flg[0,0,1] + + # Compute cloud moisture property, detraining cloud water + # and precipitation in overshooting layers + + # For the overshooting convection, calculate the moisture content of the entraining/detraining parcel as before. Partition convective cloud water and precipitation and detrain convective cloud water in the overshooting layers. + with computation(FORWARD), interval(1,-1): + dz = 0. + gamma = 0. + qrch = 0. + tem = 0. + tem1 = 0. + factor = 0. + dq = 0. + etah = 0. + ptem = 0. + qlk = 0. + dp = 0. + + if (cnvflg): + if(k_idx >= ktcon and k_idx < ktcon1): + dz = zi - zi[0,0,-1] + gamma = el2orc * qeso / (to**2) + qrch = qeso + gamma * dbyo / (hvap * (1. + gamma)) +#j + tem = 0.5 * (xlamue+xlamue[0,0,-1]) * dz + tem1 = 0.5 * xlamud * dz + factor = 1. + tem - tem1 + qcko = ((1.-tem1)*qcko[0,0,-1]+tem*0.5* + (qo+qo[0,0,-1]))/factor + qrcko = qcko +#j + dq = eta * (qcko - qrch) + + # Check if there is excess moisture to release latent heat + if(dq > 0.): + etah = .5 * (eta + eta[0,0,-1]) + dp = 1000. * del0 + if(ncloud > 0): + ptem = c0t + c1 + qlk = dq / (eta + etah * ptem * dz) + dellal = etah * c1 * dz * qlk * g / dp + else: + qlk = dq / (eta + etah * c0t * dz) + + qcko = qlk + qrch + pwo = etah * c0t * dz * qlk + cnvwt = etah * qlk * g / dp + + # Compute updraft velocity square(wu2) + # Calculate updraft velocity square(wu2) according to Han et al.'s + # (2017) \cite han_et_al_2017 equation 7. + with computation(FORWARD), interval(1,-1): + dz = 0. + tem = 0. + tem1 = 0. + ptem = 0. + ptem1 = 0. + #bb1 = 4.0 + #bb2 = 0.8 + if (cnvflg): + if(k_idx > kbcon1 and k_idx < ktcon): + dz = zi - zi[0,0,-1] + tem = 0.25 * 4.0 * (drag+drag[0,0,-1]) * dz + tem1 = 0.5 * 0.8 * (buo+buo[0,0,-1]) * dz + ptem = (1. - tem) * wu2[0,0,-1] + ptem1 = 1. + tem + wu2 = (ptem + tem1) / ptem1 + wu2 = wu2 if(wu2 > 0.) else 0. + + # Compute updraft velocity averaged over the whole cumulus + with computation(PARALLEL), interval(...): + wc = 0. + sumx = 0. + + with computation(FORWARD), interval(1,-1): + dz = 0. + tem = 0. + wc = wc[0,0,-1] + sumx = sumx[0,0,-1] + + if (cnvflg): + if(k_idx > kbcon1 and k_idx < ktcon): + dz = zi - zi[0,0,-1] + tem = 0.5 * ((wu2)**0.5 + (wu2[0,0,-1])**0.5) + wc = wc + tem * dz + sumx = sumx + dz + + # To make all slices like final slice + with computation(FORWARD), interval(-1,None): + wc = wc[0,0,-1] + sumx = sumx[0,0,-1] + + with computation(BACKWARD), interval(0,-1): + wc = wc[0,0,1] + sumx = sumx[0,0,1] + + with computation(PARALLEL), interval(...): + + if(cnvflg): + if(sumx == 0.): + cnvflg=0 + else: + wc = wc / sumx + + #val = 1.e-4 + if (wc < 1.e-4): + cnvflg = 0 + + # Exchange ktcon with ktcon1 + with computation(PARALLEL), interval(...): + kk = 1 + if(cnvflg): + kk = ktcon + ktcon = ktcon1 + ktcon1 = kk + + +## This section is ready for cloud water +## if(ncloud > 0): +## pass +@gtscript.stencil(backend=BACKEND, rebuild=REBUILD) +def stencil_static13( + cnvflg : FIELD_INT, + k_idx : FIELD_INT, + ktcon : FIELD_INT, + qeso : FIELD_FLOAT, + to : FIELD_FLOAT, + dbyo : FIELD_FLOAT, + qcko : FIELD_FLOAT, + qlko_ktcon: FIELD_FLOAT ): + + with computation(FORWARD), interval(1, None): + gamma = 0. + qrch = 0. + dq = 0. + + if cnvflg == 1: + + qlko_ktcon = qlko_ktcon[0, 0, -1] + if k_idx == ktcon - 1: + gamma = el2orc * qeso / (to*to) + qrch = qeso + gamma * dbyo / (hvap * (1. + gamma)) + dq = qcko - qrch + # Check if there is excess moisture to release latent heat + if(dq > 0.): + qlko_ktcon = dq + qcko = qrch + + with computation(BACKWARD), interval(0, -1): + qlko_ktcon = qlko_ktcon[0, 0, 1] + + +## endif + +## Compute precipitation efficiency in terms of windshear +## pass +@gtscript.stencil(backend=BACKEND, rebuild=REBUILD) +def stencil_static14( + cnvflg: FIELD_INT, + vshear: FIELD_FLOAT, + k_idx : FIELD_INT, + kb : FIELD_INT, + ktcon : FIELD_INT, + uo : FIELD_FLOAT, + vo : FIELD_FLOAT, + zi : FIELD_FLOAT, + edt : FIELD_FLOAT ): + + with computation(PARALLEL), interval(...): + if(cnvflg): + vshear = 0. + + with computation(FORWARD), interval(1,None): + vshear = vshear[0,0,-1] + if (cnvflg): + if(k_idx > kb and k_idx <= ktcon): + #shear = ((uo-uo[0,0,-1]) ** 2 \ + # + (vo-vo[0,0,-1]) ** 2)**0.5 + vshear = vshear + ((uo-uo[0,0,-1]) ** 2 + + (vo-vo[0,0,-1]) ** 2)**0.5 + + # To make all slice like final slice + with computation(BACKWARD), interval(0,-1): + vshear = vshear[0,0,1] + + with computation(FORWARD), interval(...): + zi_kb = zi + zi_ktcon = zi + + if(k_idx != 1): + zi_kb = zi_kb[0,0,-1] + zi_ktcon = zi_ktcon[0,0,-1] + + if(k_idx == kb): + zi_kb = zi + + if(k_idx == ktcon): + zi_ktcon = zi + + with computation(BACKWARD), interval(0,-1): + zi_kb = zi_kb[0,0,1] + zi_ktcon = zi_ktcon[0,0,1] + + with computation(PARALLEL), interval(...): + if(cnvflg): + # Use ziktcon and zikb to represent zi(ktcon) and zi(kb) + vshear = 1.e3 * vshear / (zi_ktcon-zi_kb) + + #e1 = 1.591-.639*vshear \ + # + .0953*(vshear**2)-.00496*(vshear**3) + + edt = 1.-(1.591-.639*vshear + +.0953*(vshear**2)-.00496*(vshear**3)) + #val = .9 + edt = edt if(edt < .9) else .9 + #val = .0 + edt = edt if(edt > .0) else .0 diff --git a/projects2020/group05/shalconv/kernels/stencils_part34.py b/projects2020/group05/shalconv/kernels/stencils_part34.py new file mode 100644 index 00000000..91722d2e --- /dev/null +++ b/projects2020/group05/shalconv/kernels/stencils_part34.py @@ -0,0 +1,891 @@ +import gt4py as gt +from gt4py import gtscript +from gt4py.gtscript import PARALLEL, FORWARD, BACKWARD, computation, interval +from shalconv.funcphys import fpvsx_gt as fpvs +from shalconv.physcons import ( + con_g as g, + con_cp as cp, + con_hvap as hvap, + con_rv as rv, + con_fvirt as fv, + con_t0c as t0c, + con_rd as rd, + con_cvap as cvap, + con_cliq as cliq, + con_eps as eps, + con_epsm1 as epsm1 +) +from .utils import * +from . import * + + +@gtscript.stencil(backend=BACKEND, rebuild=REBUILD, externals={"min": min, "max": max, "sqrt": sqrt}) +def comp_tendencies( cnvflg : FIELD_INT, + k_idx : FIELD_INT, + kmax : FIELD_INT, + kb : FIELD_INT, + ktcon : FIELD_INT, + ktcon1 : FIELD_INT, + kbcon1 : FIELD_INT, + kbcon : FIELD_INT, + dellah : FIELD_FLOAT, + dellaq : FIELD_FLOAT, + dellau : FIELD_FLOAT, + dellav : FIELD_FLOAT, + del0 : FIELD_FLOAT, + zi : FIELD_FLOAT, + zi_ktcon1 : FIELD_FLOAT, + zi_kbcon1 : FIELD_FLOAT, + heo : FIELD_FLOAT, + qo : FIELD_FLOAT, + xlamue : FIELD_FLOAT, + xlamud : FIELD_FLOAT, + eta : FIELD_FLOAT, + hcko : FIELD_FLOAT, + qrcko : FIELD_FLOAT, + uo : FIELD_FLOAT, + ucko : FIELD_FLOAT, + vo : FIELD_FLOAT, + vcko : FIELD_FLOAT, + qcko : FIELD_FLOAT, + dellal : FIELD_FLOAT, + qlko_ktcon: FIELD_FLOAT, + wc : FIELD_FLOAT, + gdx : FIELD_FLOAT, + dtconv : FIELD_FLOAT, + u1 : FIELD_FLOAT, + v1 : FIELD_FLOAT, + po : FIELD_FLOAT, + to : FIELD_FLOAT, + tauadv : FIELD_FLOAT, + xmb : FIELD_FLOAT, + sigmagfm : FIELD_FLOAT, + garea : FIELD_FLOAT, + scaldfunc : FIELD_FLOAT, + xmbmax : FIELD_FLOAT, + sumx : FIELD_FLOAT, + umean : FIELD_FLOAT, + *, + g : DTYPE_FLOAT, + betaw : DTYPE_FLOAT, + dtmin : DTYPE_FLOAT, + dt2 : DTYPE_FLOAT, + dtmax : DTYPE_FLOAT, + dxcrt : DTYPE_FLOAT ): + + # Calculate the change in moist static energy, moisture + # mixing ratio, and horizontal winds per unit cloud base mass + # flux for all layers below cloud top from equations B.14 + # and B.15 from Grell (1993) \cite grell_1993, and for the + # cloud top from B.16 and B.17 + + # Initialize zi_ktcon1 and zi_kbcon1 fields (propagate forward) + with computation(FORWARD), interval(...): + + if k_idx == ktcon1: + zi_ktcon1 = zi + elif k_idx > 0: + zi_ktcon1 = zi_ktcon1[0, 0, -1] + + if k_idx == kbcon1: + zi_kbcon1 = zi + elif k_idx > 0: + zi_kbcon1 = zi_kbcon1[0, 0, -1] + + # Initialize zi_ktcon1 and zi_kbcon1 fields (propagate backward) + with computation(BACKWARD), interval(0, -1): + + zi_ktcon1 = zi_ktcon1[0, 0, 1] + zi_kbcon1 = zi_kbcon1[0, 0, 1] + + with computation(PARALLEL), interval(...): + + if cnvflg == 1 and k_idx <= kmax: + dellah = 0.0 + dellaq = 0.0 + dellau = 0.0 + dellav = 0.0 + + with computation(PARALLEL), interval(1, -1): + + dp = 0.0 + dz = 0.0 + gdp = 0.0 + + dv1h = 0.0 + dv3h = 0.0 + dv2h = 0.0 + + dv1q = 0.0 + dv3q = 0.0 + dv2q = 0.0 + + tem = 0.0 + tem1 = 0.0 + + eta_curr = 0.0 + eta_prev = 0.0 + + tem2 = 0.0 + + # Changes due to subsidence and entrainment + if cnvflg == 1 and k_idx > kb and k_idx < ktcon: + + dp = 1000.0 * del0 + dz = zi[0, 0, 0] - zi[0, 0, -1] + gdp = g/dp + + dv1h = heo[0, 0, 0] + dv3h = heo[0, 0, -1] + dv2h = 0.5 * (dv1h + dv3h) + + dv1q = qo[0, 0, 0] + dv3q = qo[0, 0, -1] + dv2q = 0.5 * (dv1q + dv3q) + + tem = 0.5 * (xlamue[0, 0, 0] + xlamue[0, 0, -1]) + tem1 = xlamud + + eta_curr = eta[0, 0, 0] + eta_prev = eta[0, 0, -1] + + dellah = dellah + ( eta_curr * dv1h - \ + eta_prev * dv3h - \ + eta_prev * dv2h * tem * dz + \ + eta_prev * tem1 * 0.5 * dz * \ + (hcko[0, 0, 0] + hcko[0, 0, -1]) ) * gdp + + dellaq = dellaq + ( eta_curr * dv1q - \ + eta_prev * dv3q - \ + eta_prev * dv2q * tem * dz + \ + eta_prev * tem1 * 0.5 * dz * \ + (qrcko[0, 0, 0] + qcko[0, 0, -1]) ) * gdp + + tem1 = eta_curr * (uo[0, 0, 0] - ucko[0, 0, 0]) + tem2 = eta_prev * (uo[0, 0, -1] - ucko[0, 0, -1]) + dellau = dellau + (tem1 - tem2) * gdp + + tem1 = eta_curr * (vo[0, 0, 0] - vcko[0, 0, 0]) + tem2 = eta_prev * (vo[0, 0, -1] - vcko[0, 0, -1]) + dellav = dellav + (tem1 - tem2) * gdp + + with computation(PARALLEL), interval(1,None): + + tfac = 0.0 + + # Cloud top + if cnvflg == 1: + + if ktcon == k_idx: + + dp = 1000.0 * del0 + gdp = g/dp + + dv1h = heo[0, 0, -1] + dellah = eta[0, 0, -1] * (hcko[0, 0, -1] - dv1h) * gdp + + dv1q = qo [0, 0, -1] + dellaq = eta[0, 0, -1] * (qcko[0, 0, -1] - dv1q) * gdp + + dellau = eta[0, 0, -1] * (ucko[0, 0, -1] - uo[0, 0, -1]) * gdp + dellav = eta[0, 0, -1] * (vcko[0, 0, -1] - vo[0, 0, -1]) * gdp + + # Cloud water + dellal = eta[0, 0, -1] * qlko_ktcon * gdp + + with computation(PARALLEL), interval(...): + + # Following Bechtold et al. (2008) \cite + # bechtold_et_al_2008, calculate the convective turnover + # time using the mean updraft velocity (wc) and the cloud + # depth. It is also proportional to the grid size (gdx). + if cnvflg == 1: + + tem = zi_ktcon1 - zi_kbcon1 + tfac = 1.0 + gdx/75000.0 + dtconv = tfac * tem/wc + dtconv = max(dtconv, dtmin) + dtconv = max(dtconv, dt2) + dtconv = min(dtconv, dtmax) + + # Initialize field for advective time scale computation + sumx = 0.0 + umean = 0.0 + + # Calculate advective time scale (tauadv) using a mean cloud layer + # wind speed (propagate forward) + with computation(FORWARD), interval(1, -1): + + if cnvflg == 1: + if k_idx >= kbcon1 and k_idx < ktcon1: + dz = zi[0, 0, 0] - zi[0, 0, -1] + tem = (u1*u1 + v1*v1)**0.5 #sqrt(u1*u1 + v1*v1) + umean = umean[0, 0, -1] + tem * dz + sumx = sumx [0, 0, -1] + dz + else: + umean = umean[0, 0, -1] + sumx = sumx [0, 0, -1] + + # Calculate advective time scale (tauadv) using a mean cloud layer + # wind speed (propagate backward) + with computation(BACKWARD), interval(1, -2): + if cnvflg == 1: + umean = umean[0, 0, 1] + sumx = sumx [0, 0, 1] + + with computation(PARALLEL), interval(...): + + rho = 0.0 + val = 1.0 + val1 = 2.0e-4 + val2 = 6.0e-4 + val3 = 0.001 + val4 = 0.999 + val5 = 0.0 + + if cnvflg == 1: + umean = umean/sumx + umean = max(umean, val) # Passing literals (e.g. 1.0) to functions might cause errors in conditional statements + tauadv = gdx/umean + + with computation(FORWARD): + + with interval(0, 1): + + if cnvflg == 1 and k_idx == kbcon: + + # From Han et al.'s (2017) \cite han_et_al_2017 equation + # 6, calculate cloud base mass flux as a function of the + # mean updraft velocity + rho = po * 100.0 / (rd * to) + tfac = tauadv/dtconv + tfac = min(tfac, val) # Same as above: literals + xmb = tfac * betaw * rho * wc + + # For scale-aware parameterization, the updraft fraction + # (sigmagfm) is first computed as a function of the + # lateral entrainment rate at cloud base (see Han et + # al.'s (2017) \cite han_et_al_2017 equation 4 and 5), + # following the study by Grell and Freitas (2014) \cite + # grell_and_freitus_2014 + tem = max(xlamue, val1) + tem = min(tem, val2) + tem = 0.2/tem + tem1 = 3.14 * tem * tem + + sigmagfm = tem1/garea + sigmagfm = max(sigmagfm, val3) + sigmagfm = min(sigmagfm, val4) + + with interval(1, None): + + if cnvflg == 1 and k_idx == kbcon: + + # From Han et al.'s (2017) \cite han_et_al_2017 equation + # 6, calculate cloud base mass flux as a function of the + # mean updraft velocity + rho = po * 100.0 / (rd * to) + tfac = tauadv/dtconv + tfac = min(tfac, val) # Same as above: literals + xmb = tfac * betaw * rho * wc + + # For scale-aware parameterization, the updraft fraction + # (sigmagfm) is first computed as a function of the + # lateral entrainment rate at cloud base (see Han et + # al.'s (2017) \cite han_et_al_2017 equation 4 and 5), + # following the study by Grell and Freitas (2014) \cite + # grell_and_freitus_2014 + tem = max(xlamue, val1) + tem = min(tem, val2) + tem = 0.2/tem + tem1 = 3.14 * tem * tem + + sigmagfm = tem1/garea + sigmagfm = max(sigmagfm, val3) + sigmagfm = min(sigmagfm, val4) + + else: + + xmb = xmb[0, 0, -1] + sigmagfm = sigmagfm[0, 0, -1] + + with computation(BACKWARD), interval(0, -1): + + if cnvflg == 1: + xmb = xmb[0, 0, 1] + sigmagfm = sigmagfm[0, 0, 1] + + with computation(PARALLEL), interval(...): + + # Vertical convective eddy transport of mass flux as a + # function of updraft fraction from the studies by Arakawa + # and Wu (2013) \cite arakawa_and_wu_2013 (also see Han et + # al.'s (2017) \cite han_et_al_2017 equation 1 and 2). The + # final cloud base mass flux with scale-aware + # parameterization is obtained from the mass flux when + # sigmagfm << 1, multiplied by the reduction factor (Han et + # al.'s (2017) \cite han_et_al_2017 equation 2). + if cnvflg == 1: + if gdx < dxcrt: + scaldfunc = (1.0 - sigmagfm) * (1.0 - sigmagfm) + scaldfunc = min(scaldfunc, val) + scaldfunc = max(scaldfunc, val5) + else: + scaldfunc = 1.0 + + xmb = xmb * scaldfunc + xmb = min(xmb, xmbmax) + + +@gtscript.stencil(backend=BACKEND, rebuild=REBUILD) +def comp_tendencies_tr( cnvflg : FIELD_INT, + k_idx : FIELD_INT, + kmax : FIELD_INT, + kb : FIELD_INT, + ktcon : FIELD_INT, + dellae : FIELD_FLOAT, + del0 : FIELD_FLOAT, + eta : FIELD_FLOAT, + ctro : FIELD_FLOAT, + ecko : FIELD_FLOAT, + *, + g : DTYPE_FLOAT ): + + with computation(PARALLEL), interval(...): + + if cnvflg == 1 and k_idx <= kmax: + + dellae = 0.0 + + with computation(PARALLEL), interval(1, -1): + + tem1 = 0.0 + tem2 = 0.0 + dp = 0.0 + + if cnvflg == 1 and k_idx > kb and k_idx < ktcon: + + # Changes due to subsidence and entrainment + dp = 1000.0 * del0 + + tem1 = eta[0, 0, 0] * (ctro[0, 0, 0] - ecko[0, 0, 0]) + tem2 = eta[0, 0, -1] * (ctro[0, 0, -1] - ecko[0, 0, -1]) + + dellae = dellae + (tem1 - tem2) * g/dp + + with computation(PARALLEL), interval(1, None): + + # Cloud top + if cnvflg == 1 and ktcon == k_idx: + + dp = 1000.0 * del0 + + dellae = eta[0, 0, -1] * (ecko[0, 0, -1] - ctro[0, 0, -1]) * g/dp + + +@gtscript.stencil(backend=BACKEND, rebuild=REBUILD, externals={"fpvs": fpvs, "min": min, "max": max, "exp": exp, "sqrt": sqrt}) +def feedback_control_update( cnvflg : FIELD_INT, + k_idx : FIELD_INT, + kmax : FIELD_INT, + kb : FIELD_INT, + ktcon : FIELD_INT, + flg : FIELD_INT, + islimsk: FIELD_INT, + ktop : FIELD_INT, + kbot : FIELD_INT, + kbcon : FIELD_INT, + kcnv : FIELD_INT, + qeso : FIELD_FLOAT, + pfld : FIELD_FLOAT, + delhbar: FIELD_FLOAT, + delqbar: FIELD_FLOAT, + deltbar: FIELD_FLOAT, + delubar: FIELD_FLOAT, + delvbar: FIELD_FLOAT, + qcond : FIELD_FLOAT, + dellah : FIELD_FLOAT, + dellaq : FIELD_FLOAT, + t1 : FIELD_FLOAT, + xmb : FIELD_FLOAT, + q1 : FIELD_FLOAT, + u1 : FIELD_FLOAT, + dellau : FIELD_FLOAT, + v1 : FIELD_FLOAT, + dellav : FIELD_FLOAT, + del0 : FIELD_FLOAT, + rntot : FIELD_FLOAT, + delqev : FIELD_FLOAT, + delq2 : FIELD_FLOAT, + pwo : FIELD_FLOAT, + deltv : FIELD_FLOAT, + delq : FIELD_FLOAT, + qevap : FIELD_FLOAT, + rn : FIELD_FLOAT, + edt : FIELD_FLOAT, + cnvw : FIELD_FLOAT, + cnvwt : FIELD_FLOAT, + cnvc : FIELD_FLOAT, + ud_mf : FIELD_FLOAT, + dt_mf : FIELD_FLOAT, + eta : FIELD_FLOAT, + *, + dt2 : DTYPE_FLOAT, + g : DTYPE_FLOAT, + evfact : DTYPE_FLOAT, + evfactl: DTYPE_FLOAT, + el2orc : DTYPE_FLOAT, + elocp : DTYPE_FLOAT ): + + with computation(PARALLEL), interval(...): + + # Initialize flg + flg = cnvflg + + # Recalculate saturation specific humidity + qeso = 0.01 * fpvs(t1) # fpvs is in Pa + qeso = eps * qeso/(pfld + epsm1 * qeso) + val = 1.0e-8 + qeso = max(qeso, val) + + dellat = 0.0 + fpvst1 = 0.0 + + # - Calculate the temperature tendency from the moist + # static energy and specific humidity tendencies + # - Update the temperature, specific humidity, and + # horizontal wind state variables by multiplying the + # cloud base mass flux-normalized tendencies by the + # cloud base mass flux + if cnvflg == 1: + if k_idx > kb and k_idx <= ktcon: + dellat = (dellah - hvap * dellaq)/cp + t1 = t1 + dellat * xmb * dt2 + + fpvst1 = 0.01 * fpvs(t1) # fpvs is in Pa + + if cnvflg == 1: + if k_idx > kb and k_idx <= ktcon: + + q1 = q1 + dellaq * xmb * dt2 + u1 = u1 + dellau * xmb * dt2 + v1 = v1 + dellav * xmb * dt2 + + # Recalculate saturation specific humidity using the + # updated temperature + qeso = fpvst1 + qeso = eps * qeso/(pfld + epsm1 * qeso) + qeso = max(qeso, val) + + # Accumulate column-integrated tendencies (propagate forward) + with computation(FORWARD): + + # To avoid conditionals in the full interval + with interval(0, 1): + + dp = 0.0 + dpg = 0.0 + + if cnvflg == 1 and k_idx > kb and k_idx <= ktcon: + + dp = 1000.0 * del0 + dpg = dp/g + + delhbar = delhbar + dellah * xmb * dpg + delqbar = delqbar + dellaq * xmb * dpg + deltbar = deltbar + dellat * xmb * dpg + delubar = delubar + dellau * xmb * dpg + delvbar = delvbar + dellav * xmb * dpg + + with interval(1, None): + + if cnvflg == 1: + if k_idx > kb and k_idx <= ktcon: + + dp = 1000.0 * del0 + dpg = dp/g + + delhbar = delhbar[0, 0, -1] + dellah * xmb * dpg + delqbar = delqbar[0, 0, -1] + dellaq * xmb * dpg + deltbar = deltbar[0, 0, -1] + dellat * xmb * dpg + delubar = delubar[0, 0, -1] + dellau * xmb * dpg + delvbar = delvbar[0, 0, -1] + dellav * xmb * dpg + + else: + + delhbar = delhbar[0, 0, -1] + delqbar = delqbar[0, 0, -1] + deltbar = deltbar[0, 0, -1] + delubar = delubar[0, 0, -1] + delvbar = delvbar[0, 0, -1] + + with computation(BACKWARD): + + # To avoid conditionals in the full interval + with interval(-1, None): + + if cnvflg == 1: + if k_idx > kb and k_idx < ktcon: + rntot = rntot + pwo * xmb * 0.001 * dt2 + + with interval(0, -1): + if cnvflg == 1: + + # Accumulate column-integrated tendencies (propagate backward) + delhbar = delhbar[0, 0, 1] + delqbar = delqbar[0, 0, 1] + deltbar = deltbar[0, 0, 1] + delubar = delubar[0, 0, 1] + delvbar = delvbar[0, 0, 1] + + # Add up column-integrated convective precipitation by + # multiplying the normalized value by the cloud base + # mass flux (propagate backward) + if k_idx > kb and k_idx < ktcon: + + rntot = rntot[0, 0, 1] + pwo * xmb * 0.001 * dt2 + + else: + + rntot = rntot[0, 0, 1] + + # Add up column-integrated convective precipitation by + # multiplying the normalized value by the cloud base + # mass flux (propagate forward) + with computation(FORWARD), interval(1, None): + + if cnvflg == 1: + rntot = rntot[0, 0, -1] + + # - Determine the evaporation of the convective precipitation + # and update the integrated convective precipitation + # - Update state temperature and moisture to account for + # evaporation of convective precipitation + # - Update column-integrated tendencies to account for + # evaporation of convective precipitation + with computation(BACKWARD): + + with interval(-1, None): + + evef = 0.0 + dp = 0.0 + sqrt_val = 0.0 + tem = 0.0 + tem1 = 0.0 + + if k_idx <= kmax: + + deltv = 0.0 + delq = 0.0 + qevap = 0.0 + + if cnvflg == 1: + if k_idx > kb and k_idx < ktcon: + rn = rn + pwo * xmb * 0.001 * dt2 + + if flg == 1 and k_idx < ktcon: + + if islimsk == 1: + evef = edt * evfactl + else: + evef = edt * evfact + + qcond = evef * (q1 - qeso)/(1.0 + el2orc * qeso/(t1**2)) + + dp = 1000.0 * del0 + + if rn > 0.0 and qcond < 0.0: + + tem = dt2 * rn + tem = sqrt(tem) + tem = -0.32 * tem + tem = exp(tem) + qevap = -qcond * (1.0 - tem) + tem = 1000.0 * g/dp + qevap = min(qevap, tem) + delq2 = delqev + 0.001 * qevap * dp/g + + if rn > 0.0 and qcond < 0.0 and delq2 > rntot: + + qevap = 1000.0 * g * (rntot - delqev) / dp + flg = 0 + + else: + flg = flg + + if rn > 0.0 and qevap > 0.0: + + tem = 0.001 * dp/g + tem1 = qevap * tem + + if tem1 > rn: + qevap = rn/tem + rn = 0.0 + else: + rn = rn - tem1 + + q1 = q1 + qevap + t1 = t1 - elocp * qevap + deltv = -elocp * qevap/dt2 + delq = qevap/dt2 + + delqev = delqev + 0.001 * dp * qevap/g + + else: + delqev = delqev + + delqbar = delqbar + delq * dp/g + deltbar = deltbar + deltv * dp/g + + with interval(0, -1): + + rn = rn[0, 0, 1] + flg = flg[0, 0, 1] + delqev = delqev[0, 0, 1] + delqbar = delqbar[0, 0, 1] + deltbar = deltbar[0, 0, 1] + + if k_idx <= kmax: + + deltv = 0.0 + delq = 0.0 + qevap = 0.0 + + if cnvflg == 1: + if k_idx > kb and k_idx < ktcon: + rn = rn + pwo * xmb * 0.001 * dt2 + + if flg == 1 and k_idx < ktcon: + + if islimsk == 1: + evef = edt * evfactl + else: + evef = edt * evfact + + qcond = evef * (q1 - qeso)/(1.0 + el2orc * qeso/(t1**2)) + + dp = 1000.0 * del0 + + if rn > 0.0 and qcond < 0.0: + + tem = dt2 * rn + tem = sqrt(tem) + tem = -0.32 * tem + tem = exp(tem) + qevap = -qcond * (1.0 - tem) + tem = 1000.0 * g/dp + qevap = min(qevap, tem) + delq2 = delqev + 0.001 * qevap * dp/g + + if rn > 0.0 and qcond < 0.0 and delq2 > rntot: + + qevap = 1000.0 * g * (rntot - delqev) / dp + flg = 0 + + else: + flg = flg + + if rn > 0.0 and qevap > 0.0: + + tem = 0.001 * dp/g + tem1 = qevap * tem + + if tem1 > rn: + qevap = rn/tem + rn = 0.0 + else: + rn = rn - tem1 + + q1 = q1 + qevap + t1 = t1 - elocp * qevap + deltv = -elocp * qevap/dt2 + delq = qevap/dt2 + + delqev = delqev + 0.001 * dp * qevap/g + + else: + delqev = delqev + + delqbar = delqbar + delq * dp/g + deltbar = deltbar + deltv * dp/g + + + with computation(FORWARD), interval(1, None): + + rn = rn [0, 0, -1] + flg = flg[0, 0, -1] + + with computation(PARALLEL), interval(...): + + val1 = 0.0 + if cnvflg == 1 and k_idx >= kbcon and k_idx < ktcon: + val1 = 1.0 + 675.0 * eta * xmb + + val2 = 0.2 + val3 = 0.0 + val4 = 1.0e6 + cnvc_log = 0.0 + + cnvc_log = 0.04 * log(val1, val4) # 1.0e6 seems to get reasonable results, since val1 is on average ~50 + + if cnvflg == 1: + + if rn < 0.0 or flg == 0: rn = 0.0 + + ktop = ktcon + kbot = kbcon + kcnv = 2 + + if k_idx >= kbcon and k_idx < ktcon: + + # Calculate shallow convective cloud water + cnvw = cnvwt * xmb * dt2 + + # Calculate convective cloud cover, which is used when + # pdf-based cloud fraction is used + cnvc = min(cnvc_log, val2) + cnvc = max(cnvc, val3) + + # Calculate the updraft convective mass flux + if k_idx >= kb and k_idx < ktop: + ud_mf = eta * xmb * dt2 + + # Save the updraft convective mass flux at cloud top + if k_idx == ktop - 1: + dt_mf = ud_mf + + +@gtscript.stencil(backend=BACKEND, rebuild=REBUILD) +def feedback_control_upd_trr( cnvflg : FIELD_INT, + k_idx : FIELD_INT, + kmax : FIELD_INT, + ktcon : FIELD_INT, + del0 : FIELD_FLOAT, + delebar: FIELD_FLOAT, + ctr : FIELD_FLOAT, + dellae : FIELD_FLOAT, + xmb : FIELD_FLOAT, + qtr : FIELD_FLOAT, + *, + dt2 : DTYPE_FLOAT, + g : DTYPE_FLOAT ): + + with computation(PARALLEL), interval(...): + delebar = 0.0 + + if cnvflg == 1 and k_idx <= kmax and k_idx <= ktcon: + + ctr = ctr + dellae * xmb * dt2 + qtr = ctr + + # Propagate forward delebar values + with computation(FORWARD): + + with interval(0, 1): + + dp = 0.0 + + if cnvflg == 1 and k_idx <= kmax and k_idx <= ktcon: + dp = 1000.0 * del0 + + delebar = delebar + dellae * xmb * dp/g # Where does dp come from? Is it correct to use the last value at line 1559 of samfshalcnv.F? + + with interval(1, None): + + if cnvflg == 1: + + dp = 1000.0 * del0 + + if k_idx <= kmax and k_idx <= ktcon: + delebar = delebar[0, 0, -1] + dellae * xmb * dp/g + else: + delebar = delebar[0, 0, -1] + dellae * xmb * dp/g + + # Propagate backward delebar values + with computation(BACKWARD), interval(0, -1): + + if cnvflg == 1: + delebar = delebar[0, 0, 1] + + +@gtscript.stencil(backend=BACKEND, rebuild=REBUILD) +def store_aero_conc( cnvflg: FIELD_INT, + k_idx : FIELD_INT, + kmax : FIELD_INT, + rn : FIELD_FLOAT, + qtr : FIELD_FLOAT, + qaero : FIELD_FLOAT ): + + with computation(PARALLEL), interval(...): + + # Store aerosol concentrations if present + if cnvflg == 1 and rn > 0.0 and k_idx <= kmax: + qtr = qaero + + +@gtscript.stencil(backend=BACKEND, rebuild=REBUILD, externals={"max": max, "min": min}) +def separate_detrained_cw( cnvflg: FIELD_INT, + k_idx : FIELD_INT, + kbcon : FIELD_INT, + ktcon : FIELD_INT, + dellal: FIELD_FLOAT, + xmb : FIELD_FLOAT, + t1 : FIELD_FLOAT, + qtr_1 : FIELD_FLOAT, + qtr_0 : FIELD_FLOAT, + *, + dt2 : DTYPE_FLOAT, + tcr : DTYPE_FLOAT, + tcrf : DTYPE_FLOAT ): + + with computation(PARALLEL), interval(...): + + # Separate detrained cloud water into liquid and ice species as + # a function of temperature only + + tem = 0.0 + val1 = 1.0 + val2 = 0.0 + tem1 = 0.0 + + if cnvflg == 1 and k_idx >= kbcon and k_idx <= ktcon: + + tem = dellal * xmb * dt2 + tem1 = (tcr - t1) * tcrf + tem1 = min(val1, tem1) + tem1 = max(val2, tem1) + + if qtr_1 > -999.0: + qtr_0 = qtr_0 + tem * tem1 + qtr_1 = qtr_1 + tem * (1.0 - tem1) + else: + qtr_0 = qtr_0 + tem + + +@gtscript.stencil(backend=BACKEND, rebuild=REBUILD, externals={"max": max}) +def tke_contribution( cnvflg : FIELD_INT, + k_idx : FIELD_INT, + kb : FIELD_INT, + ktop : FIELD_INT, + eta : FIELD_FLOAT, + xmb : FIELD_FLOAT, + pfld : FIELD_FLOAT, + t1 : FIELD_FLOAT, + sigmagfm: FIELD_FLOAT, + qtr_ntk : FIELD_FLOAT, + *, + betaw : DTYPE_FLOAT ): + + with computation(PARALLEL), interval(1, -1): + + tem = 0.0 + tem1 = 0.0 + ptem = 0.0 + + # Include TKE contribution from shallow convection + if cnvflg == 1 and k_idx > kb and k_idx < ktop: + + tem = 0.5 * (eta[0, 0, -1] + eta[0, 0, 0]) * xmb + tem1 = pfld * 100.0/(rd * t1) + sigmagfm = max(sigmagfm, betaw) + ptem = tem/(sigmagfm * tem1) + qtr_ntk = qtr_ntk + 0.5 * sigmagfm * ptem * ptem diff --git a/projects2020/group05/shalconv/kernels/utils.py b/projects2020/group05/shalconv/kernels/utils.py new file mode 100644 index 00000000..e0f288e9 --- /dev/null +++ b/projects2020/group05/shalconv/kernels/utils.py @@ -0,0 +1,65 @@ +import gt4py as gt +from gt4py import gtscript +from gt4py.gtscript import PARALLEL, FORWARD, BACKWARD, interval, computation +import numpy as np +from shalconv import BACKEND, REBUILD, FIELD_INT, FIELD_FLOAT, DTYPE_INT, DTYPE_FLOAT + +########################### USEFUL FUNCTIONS ########################### +# These should be moved in a separate file to avoid cluttering and be +# reused in other places! +######################################################################## + +@gtscript.function +def sqrt(x): + return x**0.5 + + +@gtscript.function +def exp(x): + return np.e**x + + +@gtscript.function +def min(x, y): + return x if x <= y else y + + +@gtscript.function +def max(x, y): + return x if x >= y else y + + +@gtscript.function +def log(x, a): + return a * (x**(1.0/a)) - a + + +def slice_to_3d(slice): + return slice[np.newaxis, :, :] + + +def exit_routine(cnvflg, im): + cnvflg.synchronize() + cnvflg_np = cnvflg[0,:im,0].view(np.ndarray) + return cnvflg_np.sum() == 0 + + +@gtscript.stencil(backend=BACKEND, rebuild=REBUILD) +def get_1D_from_index( + infield : FIELD_FLOAT, #2D array X + outfield: FIELD_FLOAT, #1D array X[i,ind(i)] + ind : FIELD_INT, #1D array ind + k_idx : FIELD_INT #1D array k_idx +): + with computation(FORWARD), interval(0, 1): + outfield = infield + + with computation(FORWARD), interval(1, None): + outfield = infield + if (k_idx > ind): + outfield = outfield[0, 0, -1] + + with computation(BACKWARD), interval(0, -1): + outfield = outfield[0, 0, 1] + +######################################################################## diff --git a/projects2020/group05/shalconv/physcons.py b/projects2020/group05/shalconv/physcons.py new file mode 100644 index 00000000..f07fb9c4 --- /dev/null +++ b/projects2020/group05/shalconv/physcons.py @@ -0,0 +1,85 @@ +import numpy as np + + +### Math constants ### +con_pi = np.pi # Pi +con_e = np.e # e +con_sqrt2 = np.sqrt(2) # Square root of 2 +con_sqrt3 = np.sqrt(3) # Square root of 3 + + +### Geophysics/Astronomy constants ### +con_rerth = 6.3712e+6 # Radius of Earth +con_g = 9.80665 # Gravity acceleration +con_omega = 7.2921e-5 # Angular velocity of Earth +con_p0 = 1.01325e+5 # Standard atmosphere pressure +con_solr_old = 1.3660e+3 # Solar constant old +con_solr = 1.3608e+3 # Solar constant + + +### Thermodynamics constants ### +con_rgas = 8.314472 # Molar gas constant +con_rd = 2.8705e+2 # Gas constant air +con_rv = 4.6150e+2 # Gas constant H2O +con_cp = 1.0046e+3 # Specific heat of air at p +con_cv = 7.1760e+2 # Specific heat of air at v +con_cvap = 1.8460e+3 # Specific heat of H2O gas +con_cliq = 4.1855e+3 # Specific heat of H2O liquid +con_csol = 2.1060e+3 # Specific heat of H2O ice +con_hvap = 2.5000e+6 # Latent heat of H2O condensation +con_hfus = 3.3358e+5 # Latent heat of H2O fusion +con_psat = 6.1078e+2 # Pressure at H2O 3pt +con_t0c = 2.7315e+2 # Temperature at 0C in Kelvin +con_ttp = 2.7316e+2 # Temperature at H2O 3pt +con_tice = 2.7120e+2 # Temperature of freezing sea +con_jcal = 4.1855 # Joules per calorie +con_rhw0 = 1022.0 # Sea water reference density +con_epsq = 1.0e-12 # Min q for computing precipitation type + + +### Secondary constants ### +con_rocp = con_rd/con_cp +con_cpor = con_cp/con_rd +con_rog = con_rd/con_g +con_fvirt = con_rv/con_rd - 1. +con_eps = con_rd/con_rv +con_epsm1 = con_rd/con_rv - 1. +con_dldtl = con_cvap - con_cliq +con_dldti = con_cvap - con_csol +con_xponal = -con_dldtl/con_rv +con_xponbl = -con_dldtl/con_rv + con_hvap/(con_rv * con_ttp) +con_xponai = -con_dldti/con_rv +con_xponbi = -con_dldti/con_rv + (con_hvap + con_hfus)/(con_rv * con_ttp) + + +### Physics/Chemistry constants ### +con_c = 2.99792458e+8 # Speed of light +con_plnk = 6.6260693e-34 # Planck constant +con_boltz = 1.3806505e-23 # Boltzmann constant +con_sbc = 5.670400e-8 # Stefan-Boltzmann constant +con_avgd = 6.0221415e+23 # Avogadro constant +con_gasv = 22413.996e-6 # Vol. of ideal gas at 273.15K, 101.325kPa +con_amd = 28.9644 # Molecular weight of dry air +con_amw = 18.0154 # Molecular weight of water vapor +con_amo3 = 47.9982 # Molecular weight of O3 +con_amco2 = 44.011 # Molecular weight of CO2 +con_amo2 = 31.9999 # Molecular weight of O2 +con_amch4 = 16.043 # Molecular weight of CH4 +con_amn2o = 44.013 # Molecular weight of N2O +con_thgni = -38.15 # Temperature the H.G.Nuc. ice starts +cimin = 0.15 # Minimum ice concentration +qamin = 1.0e-16 # Minimum aerosol concentration + + +### Miscellaneous physics related constants +rlapse = 0.65e-2 +cb2mb = 10.0 +pa2mb = 0.01 +rhowater = 1000. # Density of water in kg/m³ +rhosnow = 100. # Density of snow in kg/m³ +rhoair = 1.28 # Density of air near the surface in kg/m³ +PQ0 = 379.90516 +A2A = 17.2693882 +A3 = 273.16 +A4 = 35.86 +RHmin = 1.0e-6 diff --git a/projects2020/group05/shalconv/samfaerosols.py b/projects2020/group05/shalconv/samfaerosols.py new file mode 100644 index 00000000..cdc633a1 --- /dev/null +++ b/projects2020/group05/shalconv/samfaerosols.py @@ -0,0 +1,328 @@ +from .physcons import con_g as g, qamin +from . import DTYPE_FLOAT, BACKEND +import gt4py as gt +from gt4py import gtscript +from __gtscript__ import PARALLEL, FORWARD, BACKWARD, computation, interval +import numpy as np + +## Constants +epsil = 1e-22 # prevent division by zero +escav = 0.8 # wet scavenging efficiency + +@gtscript.function +def set_qaero(qtr, kmax, index_k): + qaero = max(qamin, qtr) if index_k <= kmax else 0.0 + return qaero + +@gtscript.function +def set_xmbp(xmb, delp): + xmbp = g * xmb / delp + return xmbp + +@gtscript.function +def set_ctro2(qaero, kmax, index_k): + if index_k + 1 <= kmax: + ctro2 = 0.5 * (qaero[0, 0, 0] + qaero[0, 0, 1]) + else: + ctro2 = qaero # boundary already set in qaero + #if index_k == kmax: + # ctro2 = qaero + #else: + # ctro2 = 0.0 + return ctro2 + +@gtscript.function +def set_ecko2(ctro2, cnvflg, kb, index_k): + if cnvflg and (index_k <= kb): + ecko2 = ctro2 + else: + ecko2 = 0.0 + return ecko2 + +@gtscript.stencil(backend = BACKEND) +def set_work_arrays( + qtr: gtscript.Field[DTYPE_FLOAT], + xmb: gtscript.Field[DTYPE_FLOAT], + delp: gtscript.Field[DTYPE_FLOAT], + kmax: gtscript.Field[int], + kb: gtscript.Field[int], + cnvflg: gtscript.Field[int], + index_k: gtscript.Field[int], + qaero: gtscript.Field[DTYPE_FLOAT], + xmbp: gtscript.Field[DTYPE_FLOAT], + ctro2: gtscript.Field[DTYPE_FLOAT], + ecko2: gtscript.Field[DTYPE_FLOAT] +): + with computation(PARALLEL), interval(...): + qaero = set_qaero(qtr, kmax, index_k) + xmbp = set_xmbp(xmb, delp) + with computation(PARALLEL), interval(...): + ctro2 = set_ctro2(qaero, kmax, index_k) + ecko2 = set_ecko2(ctro2, cnvflg, kb, index_k) + +@gtscript.stencil(backend = BACKEND) +def calc_ecko2_chem_c_dellae2( + zi: gtscript.Field[DTYPE_FLOAT], + xlamue: gtscript.Field[DTYPE_FLOAT], + xlamud: gtscript.Field[DTYPE_FLOAT], + ctro2: gtscript.Field[DTYPE_FLOAT], + c0t: gtscript.Field[DTYPE_FLOAT], + eta: gtscript.Field[DTYPE_FLOAT], + xmbp: gtscript.Field[DTYPE_FLOAT], + kb: gtscript.Field[int], + ktcon: gtscript.Field[int], + cnvflg: gtscript.Field[int], + index_k: gtscript.Field[int], + ecko2: gtscript.Field[DTYPE_FLOAT], + chem_c: gtscript.Field[DTYPE_FLOAT], + # chem_pw: gtscript.Field[DTYPE_FLOAT], + dellae2: gtscript.Field[DTYPE_FLOAT], + *, + fscav: float +): + with computation(FORWARD), interval(1, -1): + if cnvflg and (index_k > kb) and (index_k < ktcon): + dz = zi[0, 0, 0] - zi[0, 0, -1] + tem = 0.5 * (xlamue[0, 0, 0] + xlamue[0, 0, -1]) * dz + tem1 = 0.25 * (xlamud[0, 0, 0] + xlamud[0, 0, 0]) * dz + factor = 1.0 + tem - tem1 + + # if conserved (not scavenging) then + ecko2 = ((1.0 - tem1) * ecko2[0, 0, -1] + + 0.5 * tem * (ctro2[0, 0, 0] + ctro2[0, 0, -1])) / factor + # how much will be scavenged + # this choice was used in GF, and is also described in a + # successful implementation into CESM in GRL (Yu et al. 2019), + # it uses dimesnsionless scavenging coefficients (fscav), + # but includes henry coeffs with gas phase chemistry + # fraction fscav is going into liquid + chem_c = escav * fscav * ecko2 + # of that part is going into rain out (chem_pw) + tem2 = chem_c / (1.0 + c0t * dz) + # chem_pw = c0t * dz * tem2 * eta # etah + ecko2 = tem2 + ecko2 - chem_c + with computation(PARALLEL), interval(0, -1): + if index_k >= ktcon: + ecko2 = ctro2 + if cnvflg and (index_k == ktcon): + #for the subsidence term already is considered + dellae2 = eta[0, 0, -1] * ecko2[0, 0, -1] * xmbp + +@gtscript.stencil(backend = BACKEND) +def calc_dtime_max_arr( + delp: gtscript.Field[DTYPE_FLOAT], + ktcon: gtscript.Field[int], + index_k: gtscript.Field[int], + dtime_max_arr: gtscript.Field[DTYPE_FLOAT], + *, + delt: float +): + with computation(FORWARD): + with interval(0, 1): + dtime_max_arr = delt + with interval(1, -1): + if index_k - 1 < ktcon: + dtime_max_arr = min(dtime_max_arr[0, 0, -1], 0.5 * delp[0, 0, -1]) + else: + dtime_max_arr = dtime_max_arr[0, 0, -1] + +@gtscript.stencil(backend = BACKEND) +def calc_detrainment_entrainment( + zi: gtscript.Field[DTYPE_FLOAT], + xlamue: gtscript.Field[DTYPE_FLOAT], + xlamud: gtscript.Field[DTYPE_FLOAT], + ecko2: gtscript.Field[DTYPE_FLOAT], + ctro2: gtscript.Field[DTYPE_FLOAT], + eta: gtscript.Field[DTYPE_FLOAT], + xmbp: gtscript.Field[DTYPE_FLOAT], + kb: gtscript.Field[int], + ktcon: gtscript.Field[int], + cnvflg: gtscript.Field[int], + index_k: gtscript.Field[int], + dellae2: gtscript.Field[DTYPE_FLOAT] +): + with computation(PARALLEL), interval(1, -1): + if cnvflg and (index_k < ktcon): + dz = zi[0, 0, 0] - zi[0, 0, -1] + aup = 1.0 if index_k > kb else 0.0 + + dv1q = 0.5 * (ecko2[0, 0, 0] + ecko2[0, 0, -1]) + dv2q = 0.5 * (ctro2[0, 0, 0] + ctro2[0, 0, -1]) + + tem = 0.5 * (xlamue[0, 0, 0] + xlamue[0, 0, -1]) + tem1 = 0.5 * (xlamud[0, 0, 0] + xlamud[0, 0, 0]) + + dellae2 = dellae2 + ( + aup * tem1 * eta[0, 0, -1] * dv1q # detrainment from updraft + - aup * tem * eta[0, 0, -1] * dv2q # entrainment into up and downdraft + ) * dz * xmbp + + if index_k == kb: + dellae2 = dellae2 - eta * ctro2 * xmbp + with computation(FORWARD), interval(0, 1): + if cnvflg and (kb == 1): + dellae2 = dellae2 - eta * ctro2 * xmbp + +@gtscript.stencil(backend = BACKEND) +def calc_mass_flux( + eta: gtscript.Field[DTYPE_FLOAT], + xmb: gtscript.Field[DTYPE_FLOAT], + qaero: gtscript.Field[DTYPE_FLOAT], + delp: gtscript.Field[DTYPE_FLOAT], + kb: gtscript.Field[int], + ktcon: gtscript.Field[int], + cnvflg: gtscript.Field[int], + index_k: gtscript.Field[int], + flx_lo: gtscript.Field[DTYPE_FLOAT], + totlout: gtscript.Field[DTYPE_FLOAT], + clipout: gtscript.Field[DTYPE_FLOAT], + dellae2: gtscript.Field[DTYPE_FLOAT], + *, + dtime_max: float +): + with computation(PARALLEL), interval(1, -1): + if cnvflg and (index_k - 1 < ktcon): + tem = 0.0 if index_k - 1 < kb else eta[0, 0, -1] + # low-order flux, upstream + qaero_val = qaero[0, 0, 0] if tem > 0.0 else qaero[0, 0, -1] + flx_lo = - xmb * tem * qaero_val + # make sure low-ord fluxes don't violate positive-definiteness + with computation(PARALLEL), interval(0, -1): + if cnvflg and (index_k <= ktcon): + # time step / grid spacing + dtovdz = g * dtime_max / abs(delp) + # total flux out + totlout = max(0.0, flx_lo[0, 0, 1]) - min(0.0, flx_lo[0, 0, 0]) + clipout = min(1.0, qaero / max(epsil, totlout) / (1.0001 * dtovdz)) + # recompute upstream mass fluxes + with computation(PARALLEL), interval(1, -1): + if cnvflg and index_k - 1 <= ktcon: + tem = 0.0 if index_k - 1 < kb else eta[0, 0, -1] + clipout_val = clipout[0, 0, 0] if tem > 0.0 else clipout[0, 0, -1] + flx_lo = flx_lo * clipout_val + # a positive-definite low-order (diffusive) solution for the subsidnce fluxes + with computation(PARALLEL), interval(0, -1): + if cnvflg and index_k <= ktcon: + # time step / grid spacing + dtovdz = g * dtime_max / abs(delp) + dellae2 = dellae2 - (flx_lo[0, 0, 1] - flx_lo[0, 0, 0]) * dtovdz / dtime_max + +@gtscript.stencil(backend = BACKEND) +def calc_final( + dellae2: gtscript.Field[DTYPE_FLOAT], + kmax: gtscript.Field[int], + ktcon: gtscript.Field[int], + cnvflg: gtscript.Field[int], + index_k: gtscript.Field[int], + qaero: gtscript.Field[DTYPE_FLOAT], + *, + delt: float +): + # compute final aerosol concentrations + with computation(PARALLEL), interval(...): + if cnvflg and (index_k <= min(kmax, ktcon)): + qaero = qaero + dellae2 * delt + if qaero < 0.0: + qaero = qamin + +def samfshalcnv_aerosols(im, ix, km, itc, ntc, ntr, delt, + cnvflg, kb, kmax, kbcon, ktcon, fscav_np, + xmb, c0t, eta, zi, xlamue, xlamud, delp, + qtr_np, qaero_np): + """ + Aerosol process in shallow convection + :param im: horizontal loop extent + :param ix: horizontal dimension (im <= ix) + :param km: vertical layer dimension + :param itc: number of aerosol tracers transported/scavenged by convection + :param ntc: number of chemical tracers + :param ntr: number of tracers for scale-aware mass flux schemes + :param delt: physics time step + :param cnvflg: (im) flag of convection + :param kb: (im) + :param kmax: (im) + :param kbcon: (im) + :param ktcon: (im) + :param fscav_np: (ntc) numpy array of aerosol scavenging coefficients + :param xmb: (im) + :param c0t: (im,km) Cloud water parameters + :param eta: (im,km) + :param zi: (im,km) height + :param xlamue: (im,km) + :param xlamud: (im) + :param delp: (ix,km) pressure? + :param qtr_np: (ix,km,ntr+2) numpy array + :param qaero_np: (im,km,ntc) numpy array + """ + shape_2d = (1, ix, km) + default_origin = (0, 0, 0) + # Initialization + xmbp = gt.storage.empty(BACKEND, default_origin, shape_2d, dtype = DTYPE_FLOAT) + ## Chemical transport variables (2D slices) + qtr = gt.storage.empty(BACKEND, default_origin, shape_2d, dtype = DTYPE_FLOAT) + qaero = gt.storage.empty(BACKEND, default_origin, shape_2d, dtype = DTYPE_FLOAT) + ctro2 = gt.storage.empty(BACKEND, default_origin, shape_2d, dtype = DTYPE_FLOAT) + ecko2 = gt.storage.empty(BACKEND, default_origin, shape_2d, dtype = DTYPE_FLOAT) + ecdo2 = gt.storage.empty(BACKEND, default_origin, shape_2d, dtype = DTYPE_FLOAT) + dellae2 = gt.storage.empty(BACKEND, default_origin, shape_2d, dtype = DTYPE_FLOAT) + ## Additional variables for tracers for wet deposition (2D slices) + chem_c = gt.storage.empty(BACKEND, default_origin, shape_2d, dtype = DTYPE_FLOAT) + # chem_pw = gt.storage.empty(BACKEND, default_origin, shape_2d, dtype = DTYPE_FLOAT) + # wet_dep = gt.storage.empty(BACKEND, default_origin, shape_2d, dtype = DTYPE_FLOAT) + ## Additional variables for fct + flx_lo = gt.storage.empty(BACKEND, default_origin, shape_2d, dtype = DTYPE_FLOAT) + totlout = gt.storage.empty(BACKEND, default_origin, shape_2d, dtype = DTYPE_FLOAT) + clipout = gt.storage.empty(BACKEND, default_origin, shape_2d, dtype = DTYPE_FLOAT) + ## Misc + index_ijk_np = np.indices(shape_2d) + index_k = gt.storage.from_array(index_ijk_np[2] + 1, BACKEND, default_origin, shape_2d, dtype = int) # index STARTING FROM 1 + dtime_max_arr = gt.storage.empty(BACKEND, default_origin, shape_2d, dtype = DTYPE_FLOAT) + + # Begin + ## Check if aerosols are present + if (ntc <= 0) or (itc <= 0) or (ntr <= 0): return + if (ntr < itc + ntc - 3): return + + calc_dtime_max_arr(delp, ktcon, index_k, dtime_max_arr, delt = delt) + dtime_max_arr.synchronize() + dtime_max = dtime_max_arr[0, :, -1].view(np.ndarray).min() + + ## Tracer loop + for n in range(ntc): + ## Initialize work variables + chem_c[...] = 0.0 + # chem_pw[...] = 0.0 + ctro2[...] = 0.0 + dellae2[...] = 0.0 + ecdo2[...] = 0.0 + ecko2[...] = 0.0 + qaero[...] = 0.0 + + it = n + itc - 1 + qtr[...] = qtr_np[np.newaxis, :, :, it] + set_work_arrays(qtr, xmb, delp, kmax, kb, cnvflg, + index_k, qaero, xmbp, ctro2, ecko2) + # do chemical tracers, first need to know how much reevaporates + # aerosol re-evaporation is set to zero for now + # calculate include mixing ratio (ecko2), how much goes into + # rainwater to be rained out (chem_pw), and total scavenged, + # if not reevaporated (pwav) + fscav = fscav_np[n] + calc_ecko2_chem_c_dellae2(zi, xlamue, xlamud, ctro2, c0t, eta, xmbp, kb, + ktcon, cnvflg, index_k, ecko2, chem_c, + dellae2, fscav = fscav) + + # initialize maximum allowed timestep for upstream difference approach + # MOVED OUTSIDE OF TRACER LOOP + + calc_detrainment_entrainment(zi, xlamue, xlamud, ecko2, ctro2, eta, + xmbp, kb, ktcon, cnvflg, index_k, dellae2) + + calc_mass_flux(eta, xmb, qaero, delp, kb, ktcon, cnvflg, index_k, + flx_lo, totlout, clipout, dellae2, dtime_max = dtime_max) + + calc_final(dellae2, kmax, ktcon, cnvflg, index_k, qaero, delt = delt) + + qaero.synchronize() + qaero_np[:, :, n] = qaero.view(np.ndarray)[0, :, :] + diff --git a/projects2020/group05/shalconv/samfshalcnv.py b/projects2020/group05/shalconv/samfshalcnv.py new file mode 100644 index 00000000..4faf36fd --- /dev/null +++ b/projects2020/group05/shalconv/samfshalcnv.py @@ -0,0 +1,468 @@ +import gt4py as gt +from gt4py import gtscript +# import sys +# sys.path.append("..") +from shalconv.kernels.stencils_part1 import * +from shalconv.kernels.stencils_part2 import * +from shalconv.kernels.stencils_part34 import * +from shalconv.serialization import read_data, compare_data, OUT_VARS, numpy_dict_to_gt4py_dict +from shalconv import * +from shalconv.physcons import ( + con_g as grav, + con_cp as cp, + con_hvap as hvap, + con_rv as rv, + con_fvirt as fv, + con_t0c as t0c, + con_rd as rd, + con_cvap as cvap, + con_cliq as cliq, + con_eps as eps, + con_epsm1 as epsm1, + con_e as e +) + + +def samfshalcnv_func(data_dict): + """ + Scale-Aware Mass-Flux Shallow Convection + + :param data_dict: Dict of parameters required by the scheme + :type data_dict: Dict of either scalar or gt4py storage + """ + + ############################ INITIALIZATION ############################ + + ### Input variables and arrays ### + im = data_dict["im"] + ix = data_dict["ix"] + km = data_dict["km"] + itc = data_dict["itc"] + ntc = data_dict["ntc"] + ntk = data_dict["ntk"] + ntr = data_dict["ntr"] + ncloud = data_dict["ncloud"] + clam = data_dict["clam"] + c0s = data_dict["c0s"] + c1 = data_dict["c1"] + asolfac = data_dict["asolfac"] + pgcon = data_dict["pgcon"] + delt = data_dict["delt"] + islimsk = data_dict["islimsk"] + psp = data_dict["psp"] + delp = data_dict["delp"] + prslp = data_dict["prslp"] + garea = data_dict["garea"] + hpbl = data_dict["hpbl"] + dot = data_dict["dot"] + phil = data_dict["phil"] + #fscav = data_dict["fscav"] + + ### Output buffers ### + kcnv = data_dict["kcnv"] + kbot = data_dict["kbot"] + ktop = data_dict["ktop"] + qtr = data_dict["qtr"] + q1 = data_dict["q1"] + t1 = data_dict["t1"] + u1 = data_dict["u1"] + v1 = data_dict["v1"] + rn = data_dict["rn"] + cnvw = data_dict["cnvw"] + cnvc = data_dict["cnvc"] + ud_mf = data_dict["ud_mf"] + dt_mf = data_dict["dt_mf"] + + shape = (1, ix, km) + + ### Local storages for 1D arrays (integer) ### + kpbl = gt.storage.ones(BACKEND, default_origin, shape, dtype=DTYPE_INT) + kb = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_INT) + kbcon = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_INT) + kbcon1 = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_INT) + ktcon = gt.storage.ones(BACKEND, default_origin, shape, dtype=DTYPE_INT) + ktcon1 = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_INT) + ktconn = gt.storage.ones(BACKEND, default_origin, shape, dtype=DTYPE_INT) + kbm = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_INT) + kmax = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_INT) + + ### Local storages for 1D arrays ("bool") ### + cnvflg = gt.storage.ones(BACKEND, default_origin, shape, dtype=DTYPE_INT) + flg = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_INT) + + ### Local storages for 1D arrays (float) ### + aa1 = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + cina = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + tkemean = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + clamt = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + ps = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + del0 = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + prsl = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + umean = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + tauadv = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + gdx = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + delhbar = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + delq = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + delq2 = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + delqbar = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + delqev = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + deltbar = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + deltv = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + dtconv = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + edt = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + pdot = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + po = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + qcond = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + qevap = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + hmax = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + rntot = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + vshear = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + xlamud = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + xmb = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + xmbmax = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + delubar = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + delvbar = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + c0 = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + wc = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + scaldfunc = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + sigmagfm = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + qlko_ktcon = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + sumx = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + tx1 = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + + ### Local storages for 2D arrays (float) ### + pfld = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + to = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + qo = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + uo = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + vo = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + qeso = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + wu2 = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + buo = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + drag = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + dellal = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + dbyo = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + zo = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + xlamue = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + heo = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + heso = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + dellah = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + dellaq = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + dellau = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + dellav = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + hcko = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + ucko = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + vcko = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + qcko = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + qrcko = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + eta = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + zi = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + pwo = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + c0t = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + cnvwt = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + + ### Local storages for 2D arrays (float, tracers), this will contain slices along n-axis ### + shape_2d = (im, ntr) + delebar = np.zeros(shape_2d, dtype=DTYPE_FLOAT) + + ### Local storages for 3D arrays (float, tracers), this will contain slices along n-axis ### + shape_3d = (im, km, ntr) + ctr = np.zeros(shape_3d, dtype=DTYPE_FLOAT) + ctro = np.zeros(shape_3d, dtype=DTYPE_FLOAT) + dellae = np.zeros(shape_3d, dtype=DTYPE_FLOAT) + ecko = np.zeros(shape_3d, dtype=DTYPE_FLOAT) + #qaero = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + + ### K-indices field ### + k_idx = gt.storage.from_array(np.indices(shape)[2] + 1, BACKEND, default_origin, dtype=DTYPE_INT) + + ### State buffer for 1D-2D interactions + state_buf1 = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_INT) + state_buf2 = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_INT) + + ### PART2 Specific + heo_kb = gt.storage.empty(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + dot_kbcon = gt.storage.empty(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + pfld_kbcon = gt.storage.empty(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + pfld_kb = gt.storage.empty(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + pfld_kbcon1 = gt.storage.empty(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + qtr_slice = gt.storage.empty(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + ctr_slice = gt.storage.empty(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + ctro_slice = gt.storage.empty(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + ecko_slice = gt.storage.empty(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + + ### PART3 Specific + dellae_slice = gt.storage.empty(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + zi_ktcon1 = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + zi_kbcon1 = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + + ### PART4 Specific + delebar_slice = gt.storage.empty(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + + ### Local Parameters ### + g = grav + elocp = hvap / cp + el2orc = hvap * hvap / (rv * cp) + d0 = 0.001 + cm = 1.0 + delta = fv + fact1 = (cvap - cliq) / rv + fact2 = hvap / rv - fact1 * t0c + clamd = 0.1 + tkemx = 0.65 + tkemn = 0.05 + dtke = tkemx - tkemn + dthk = 25.0 + cinpcrmx = 180.0 + cinpcrmn = 120.0 + cinacrmx = -120.0 + cinacrmn = -80.0 + crtlamd = 3.0e-4 + dtmax = 10800.0 + dtmin = 600.0 + bet1 = 1.875 + cd1 = 0.506 + f1 = 2.0 + gam1 = 0.5 + betaw = 0.03 + dxcrt = 15.0e3 + h1 = 0.33333333 + tf = 233.16 + tcr = 263.16 + tcrf = 1.0 / (tcr - tf) + + ### Determine whether to perform aerosol transport ### + do_aerosols = (itc > 0) and (ntc > 0) and (ntr > 0) + if (do_aerosols): + do_aerosols = (ntr >= itc) + + ### Compute preliminary quantities needed for the static and feedback control portions of the algorithm ### + + # Convert input Pa terms to Cb terms + pa_to_cb(psp, prslp, delp, ps, prsl, del0) + + km1 = km - 1 + + ### Initialize storages (simple initializations already done above with gt4py functionalities) ### + + # Initialize column-integrated and other single-value-per-column + # variable arrays + init_col_arr(kcnv, cnvflg, kbot, ktop, kbcon, kb, rn, gdx, garea, km=km) + + # Return to the calling routine if deep convection is present or the + # surface buoyancy flux is negative + if exit_routine(cnvflg, im): return + + # Initialize further parameters and arrays + init_par_and_arr( islimsk, c0, t1, c0t, cnvw, cnvc, ud_mf, dt_mf, + c0s=c0s, asolfac=asolfac, d0=d0) + + dt2 = delt + + # Model tunable parameters are all here + aafac = 0.05 + evfact = 0.3 + evfactl = 0.3 + w1l = -8.0e-3 + w2l = -4.0e-2 + w3l = -5.0e-3 + w4l = -5.0e-4 + w1s = -2.0e-4 + w2s = -2.0e-3 + w3s = -1.0e-3 + w4s = -2.0e-5 + + # Initialize the rest + init_kbm_kmax(kbm, k_idx, kmax, state_buf1, state_buf2, tx1, ps, prsl, km=km) + init_final( kbm, k_idx, kmax, flg, cnvflg, kpbl, tx1, + ps, prsl, zo, phil, zi, pfld, eta, hcko, qcko, + qrcko, ucko, vcko, dbyo, pwo, dellal, to, qo, + uo, vo, wu2, buo, drag, cnvwt, qeso, heo, heso, hpbl, + t1, q1, u1, v1, km=km) + + # Tracers Loop + for n in range(ntr): + + kk = n+2 + + qtr_slice[...] = qtr[np.newaxis, :, :, kk] + + # Initialize tracers. Keep in mind that, qtr slice is for the + # (n+2)-th tracer, while the other storages are slices + # representing the n-th tracer. + init_tracers( cnvflg, k_idx, kmax, ctr_slice, ctro_slice, ecko_slice, qtr_slice ) + + ctr_slice.synchronize() + ctro_slice.synchronize() + ecko_slice.synchronize() + ctr[:, :, n] = ctr_slice[0, :, :].view(np.ndarray) + ctro[:, :, n] = ctro_slice[0, :, :].view(np.ndarray) + ecko[:, :, n] = ecko_slice[0, :, :].view(np.ndarray) + + + #=======================================PART2===================================== + #================================================================================= + ### Search in the PBL for the level of maximum moist static energy to start the ascending parcel. + stencil_static0(cnvflg, hmax, heo, kb, k_idx, kpbl, kmax, zo, to, qeso, qo, po, uo, vo, heso, pfld) + + for n in range(ntr): + ctro_slice[...] = ctro[np.newaxis, :, :, n] + stencil_ntrstatic0(cnvflg, k_idx, kmax, ctro_slice) + ctro[:, :, n] = ctro_slice[0, :, :] + + ### Search below the index "kbm" for the level of free convection (LFC) where the condition. + get_1D_from_index(heo, heo_kb, kb, k_idx) + stencil_static1(cnvflg, flg, kbcon, kmax, k_idx, kbm, kb, heo_kb, heso) + + ### If no LFC, return to the calling routine without modifying state variables. + if exit_routine(cnvflg, ix): return + + ### Determine the vertical pressure velocity at the LFC. + get_1D_from_index(dot, dot_kbcon, kbcon, k_idx) + get_1D_from_index(pfld, pfld_kbcon, kbcon, k_idx) + get_1D_from_index(pfld, pfld_kb, kb, k_idx) + stencil_static2(cnvflg, pdot, dot_kbcon, islimsk, k_idx, kbcon, kb, pfld_kb, pfld_kbcon) + + ### If no LFC, return to the calling routine without modifying state variables. + if exit_routine(cnvflg, ix): return + + ### turbulent entrainment rate assumed to be proportional to subcloud mean TKE + if (ntk > 0): + qtr_ntk = gt.storage.from_array(qtr[np.newaxis, :, :, ntk - 1], BACKEND, default_origin) + stencil_static3(sumx, tkemean, cnvflg, k_idx, kb, kbcon, zo, qtr_ntk, + clamt, clam=clam) + # qtr[:,:,ntr] = qtr_ntr[0,:,:] + # else: + # stencil_static4(cnvflg, clamt, clam=clam ) + + ### assume updraft entrainment rate is an inverse function of height + stencil_static5(cnvflg, xlamue, clamt, zi, xlamud, k_idx, kbcon, kb, + eta, ktconn, kmax, kbm, hcko, ucko, vcko, heo, uo, vo) + + for n in range(ntr): + ctro_slice[...] = ctro[np.newaxis, :, :, n] + ecko_slice[...] = ecko[np.newaxis, :, :, n] + stencil_ntrstatic1(cnvflg, k_idx, kb, ecko_slice, ctro_slice) + ecko[:, :, n] = ecko_slice[0, :, :] + + stencil_static7(cnvflg, k_idx, kb, kmax, zi, xlamue, xlamud, hcko, heo, dbyo, + heso, ucko, uo, vcko, vo, pgcon=pgcon) + + for n in range(ntr): + ctro_slice[...] = ctro[np.newaxis, :, :, n] + ecko_slice[...] = ecko[np.newaxis, :, :, n] + stencil_ntrstatic2(cnvflg, k_idx, kb, kmax, zi, xlamue, ecko_slice, ctro_slice) + ecko[:, :, n] = ecko_slice[0, :, :] + + stencil_update_kbcon1_cnvflg(dbyo, cnvflg, kmax, kbm, kbcon, kbcon1, flg, k_idx) + get_1D_from_index(pfld, pfld_kbcon1, kbcon1, k_idx) + stencil_static9(cnvflg, pfld_kbcon, pfld_kbcon1) + + if exit_routine(cnvflg, ix): return + + ### calculate convective inhibition + stencil_static10(cina, cnvflg, k_idx, kb, kbcon1, zo, qeso, to, + dbyo, qo, pdot, islimsk) + + if exit_routine(cnvflg, ix): return + + dt2 = delt + stencil_static11(flg, cnvflg, ktcon, kbm, kbcon1, dbyo, kbcon, del0, xmbmax, + aa1, kb, qcko, qo, qrcko, zi, qeso, to, xlamue, + xlamud, eta, c0t, dellal, buo, drag, zo, k_idx, pwo, + cnvwt, c1=c1, dt2=dt2, ncloud=ncloud) + + if exit_routine(cnvflg, ix): return + + stencil_static12(cnvflg, aa1, flg, ktcon1, kbm, k_idx, ktcon, zo, qeso, + to, dbyo, zi, xlamue, xlamud, qcko, qrcko, qo, eta, del0, + c0t, pwo, cnvwt, buo, wu2, wc, sumx, kbcon1, drag, dellal, + c1=c1, ncloud=ncloud) + + if(ncloud > 0): + stencil_static13(cnvflg, k_idx, ktcon, qeso, to, dbyo, qcko, qlko_ktcon) + + stencil_static14(cnvflg, vshear, k_idx, kb, ktcon, uo, vo, zi, edt) + + #=======================================PART34===================================== + #================================================================================= + # Calculate the tendencies of the state variables (per unit cloud base + # mass flux) and the cloud base mass flux + comp_tendencies( cnvflg, k_idx, kmax, kb, ktcon, ktcon1, kbcon1, kbcon, + dellah, dellaq, dellau, dellav, del0, zi, zi_ktcon1, + zi_kbcon1, heo, qo, xlamue, xlamud, eta, hcko, + qrcko, uo, ucko, vo, vcko, qcko, dellal, + qlko_ktcon, wc, gdx, dtconv, u1, v1, po, to, + tauadv, xmb, sigmagfm, garea, scaldfunc, xmbmax, + sumx, umean, + g=g, betaw=betaw, dtmin=dtmin, dt2=dt2, dtmax=dtmax, dxcrt=dxcrt) + + # Calculate the tendencies of the state variables (tracers part) + for n in range(ntr): + + ctro_slice[...] = ctro[np.newaxis, :, :, n] + ecko_slice[...] = ecko[np.newaxis, :, :, n] + comp_tendencies_tr( cnvflg, k_idx, kmax, kb, ktcon, + dellae_slice, del0, eta, ctro_slice, ecko_slice, g=g ) + dellae[:, :, n] = dellae_slice[0, :, :] + + # For the "feedback control", calculate updated values of the state + # variables by multiplying the cloud base mass flux and the + # tendencies calculated per unit cloud base mass flux from the + # static control + feedback_control_update( cnvflg, k_idx, kmax, kb, ktcon, flg, + islimsk, ktop, kbot, kbcon, kcnv, qeso, + pfld, delhbar, delqbar, deltbar, delubar, + delvbar, qcond, dellah, dellaq, t1, xmb, + q1, u1, dellau, v1, dellav, del0, rntot, + delqev, delq2, pwo, deltv, delq, qevap, rn, + edt, cnvw, cnvwt, cnvc, ud_mf, dt_mf, eta, + dt2=dt2, g=g, evfact=evfact, evfactl=evfactl, + el2orc=el2orc, elocp=elocp) + + # Calculate updated values of the state variables (tracers part) + for n in range(0, ntr): + + kk = n+2 + qtr_slice[...] = qtr[np.newaxis, :, :, kk] + + ctr_slice[...] = ctr[np.newaxis, :, :, n] + dellae_slice[...] = dellae[np.newaxis, :, :, n] + + feedback_control_upd_trr( cnvflg, k_idx, kmax, ktcon, + del0, delebar_slice, ctr_slice, + dellae_slice, xmb, qtr_slice, + dt2=dt2, g=g) + + delebar[:, n] = delebar_slice[0, :, 0] + qtr[:, :, kk] = qtr_slice[0, :, :] + ctr[:, :, n] = ctr_slice[0, :, :] + + # Separate detrained cloud water into liquid and ice species as a + # function of temperature only + if ncloud > 0: + + qtr_0 = gt.storage.from_array(qtr[np.newaxis, :, :, 0], BACKEND, default_origin) + qtr_1 = gt.storage.from_array(qtr[np.newaxis, :, :, 1], BACKEND, default_origin) + + separate_detrained_cw( cnvflg, k_idx, kbcon, + ktcon, dellal, xmb, t1, qtr_1, qtr_0, + dt2=dt2, tcr=tcr, tcrf=tcrf) + + qtr[:, :, 0] = qtr_0[0, :, :] + qtr[:, :, 1] = qtr_1[0, :, :] + + # Include TKE contribution from shallow convection + if ntk > 0: + + qtr_ntk = gt.storage.from_array(qtr[np.newaxis, :, :, ntk - 1], BACKEND, default_origin) + + tke_contribution( cnvflg, k_idx, kb, ktop, + eta, xmb, pfld, t1, sigmagfm, qtr_ntk, + betaw=betaw) + + qtr[:, :, ntk - 1] = qtr_ntk[0, :, :] + + + return kcnv, kbot, ktop, q1, t1, u1, v1, rn, cnvw, cnvc, ud_mf, dt_mf, qtr + diff --git a/projects2020/group05/shalconv/serialization.py b/projects2020/group05/shalconv/serialization.py new file mode 100644 index 00000000..c7f99e23 --- /dev/null +++ b/projects2020/group05/shalconv/serialization.py @@ -0,0 +1,219 @@ +import numpy as np +import random +import gt4py as gt +from . import BACKEND, DTYPE_FLOAT, DTYPE_INT, SERIALBOX_DIR, DATAPATH +from copy import deepcopy + +import sys +sys.path.append(SERIALBOX_DIR + "/python") +import serialbox as ser + +int_vars = ["im", "ix", "km", "itc", "ntc", "ntk", "ntr", "ncloud"] +int_arrs = ['islimsk', 'kcnv', 'kbot', 'ktop', 'kpbl', 'kb', 'kbcon', + 'kbcon1', 'ktcon', 'ktcon1', 'ktconn', 'kbm', 'kmax', + 'cnvflg', 'flg'] +scalar_vars = ["im", "ix", "km", "itc", "ntc", "ntk", "ntr", "ncloud", + "clam", "c0s", "c1", "pgcon", "asolfac", "delt"] + +IN_VARS = ["im", "ix", "km", "itc", "ntc", "ntk", "ntr", "ncloud", + "clam", "c0s", "c1", "asolfac", "pgcon", "delt", "islimsk", + "psp", "delp", "prslp", "garea", "hpbl", "dot", + "phil", #"fscav", (not used) + "kcnv", "kbot", "ktop", "qtr", "q1", "t1", "u1", + "v1", "rn", "cnvw", "cnvc", "ud_mf", "dt_mf"] +OUT_VARS = ["kcnv", "kbot", "ktop", "qtr", "q1", "t1", "u1", + "v1", "rn", "cnvw", "cnvc", "ud_mf", "dt_mf"] + +def clean_numpy_dict(data_dict): + """ + Transform array with length 1 into scalar values in data dict + """ + for var in data_dict: + if var in int_vars: + data_dict[var] = DTYPE_INT(data_dict[var][0]) + elif data_dict[var].size <= 1: + data_dict[var] = DTYPE_FLOAT(data_dict[var][0]) + +def data_dict_from_var_list(var_list, serializer, savepoint): + """ + Read variables from specified savepoint in specified serializer + """ + d = {} + for var in var_list: + d[var] = serializer.read(var, savepoint) + clean_numpy_dict(d) + return d + +def numpy_dict_to_gt4py_dict(data_dict, backend = BACKEND): + """ + Transform dict of numpy arrays into dict of gt4py storages, return new dict + 1d array of shape (nx) will be transformed into storage of shape (1, nx, nz) + 2d array of shape (nx, nz) will be transformed into storage of shape (1, nx, nz) + 3d array is kept the same (numpy arrays), doing slices later + 0d array will be transformed into a scalar + """ + ix = int(data_dict["ix"])#im <= ix + km = int(data_dict["km"]) + new_data_dict = {} + + for var in data_dict: + + data = data_dict[var] + ndim = len(data.shape) + #if var == "fscav": + # data_dict["fscav"] = data # shape = (number of tracers) + + if (ndim > 0) and (ndim <= 2) and (data.size >= 2): + + default_origin = (0, 0, 0) + arrdata = np.zeros((1,ix,km)) + + if ndim == 1: # 1D array (horizontal dimension) + arrdata[...] = data[np.newaxis, :, np.newaxis] + elif ndim == 2: #2D array (horizontal dimension, vertical dimension) + arrdata[...] = data[np.newaxis, :, :] + + dtype = DTYPE_INT if var in int_arrs else DTYPE_FLOAT + new_data_dict[var] = gt.storage.from_array(arrdata, backend, default_origin, dtype = dtype) + + elif ndim == 3: #3D array qntr(horizontal dimension, vertical dimension, number of tracers) + new_data_dict[var] = deepcopy(data) + else: # scalars + new_data_dict[var] = deepcopy(data) + + return new_data_dict + +def compare_data(exp_data, ref_data): + """ + Compare two dicts of numpy arrays, raise error if one array in `exp_data` does not match the one in `ref_data` + """ + wrong = [] + flag = True + + for key in exp_data: + mask = ~np.isnan(ref_data[key]) + + if not np.allclose(exp_data[key][mask], ref_data[key][mask]): + wrong.append(key) + flag = False + else: + print(f"Successfully validate {key}!") + + assert flag, f"Data from exp and ref does not match for field {wrong}" + +def read_data(tile, is_in, path = DATAPATH, ser_count = 0): + """ + Read serialbox2 format data under `./data` folder with prefix of `Generator_rank{tile}` + :param tile: specify the number of tile in data + :type tile: int + :param is_in: true means in, false means out + :type is_in: boolean + """ + #TODO: read_async and readbuffer + serializer = ser.Serializer(ser.OpenModeKind.Read, path, "Generator_rank" + str(tile)) + inoutstr = "in" if is_in else "out" + sp = ser.Savepoint(f"samfshalcnv-{inoutstr}-{ser_count:0>6d}") + vars = IN_VARS if is_in else OUT_VARS + data = data_dict_from_var_list(vars, serializer, sp) + + return data + +def read_input_x_index(tile, ser_count, indices, path = DATAPATH): + + serializer = ser.Serializer(ser.OpenModeKind.Read, path, "Generator_rank" + str(tile)) + sp = ser.Savepoint(f"samfshalcnv-in-{ser_count:0>6d}") + vars = set(IN_VARS) - set(scalar_vars) + data = data_dict_from_var_list(vars, serializer, sp) + + for key in data: + + ndim = len(data[key].shape) + arr = data[key] + + if ndim == 1 and key != "fscav": + data[key] = arr[indices] + elif ndim == 2: + data[key] = arr[indices,:] + elif ndim == 3: + data[key] = arr[indices,:,:] + + return data + +def read_random_input(length, ix, ntile, ncount, path = DATAPATH): + """ + Generate input data of specified x dimension by randomly selecting columns from serialized data + ignore `fscav` because it doesn't have x dimension + :param length: number of columns to generate + :param ix: original x dimension in the 1 tile and 1 savepoint of serialized data + :param ntile: number of tiles in serialized data + :param ncount: number of savepoints in serialized data + :param path: path to serialized data + """ + tile = np.ndarray((length,), dtype=DTYPE_INT) + ser_count = np.ndarray((length,), dtype=DTYPE_INT) + index = np.ndarray((length,), dtype=DTYPE_INT) + + for n in range(length): + tile[n] = random.randint(0, ntile - 1) + ser_count[n] = random.randint(0, ncount - 1) + index[n] = random.randint(0, ix - 1) + + ind = np.lexsort((index, ser_count, tile)) + index = index[ind] + ser_count = ser_count[ind] + tile = tile[ind] + breaks = [] + prev = (tile[0], ser_count[0]) + for n in range(1, length): + curr = (tile[n], ser_count[n]) + + if prev != curr: + breaks.append(n) + + prev = curr + + index = np.split(index, breaks) + data_list = [] + + for i in range(len(breaks)): + tile_i = tile[breaks[i]] + ser_count_i = ser_count[breaks[i]] + index_i = index[i] + + data_list.append(read_input_x_index(tile_i, ser_count_i, index_i, path=path)) + + output = {} + for key in data_list[0]: + data = data_list[0][key] + ndim = len(data.shape) + + if key == "fscav": + output[key] = data + elif ndim == 1: + output[key] = np.zeros((length,), dtype=data.dtype, order='F') + elif ndim == 2: + output[key] = np.zeros((length, data.shape[1]), dtype=data.dtype, order='F') + elif ndim == 3: + output[key] = np.zeros((length, data.shape[1], data.shape[2]), dtype=data.dtype, order='F') + + breaks.append(length) + breaks.insert(0, 0) + + for key in output: + if key != "fscav": + for i in range(len(data_list)): + output[key][breaks[i]:breaks[i+1],...] = data_list[i][key] + + return output + +def view_gt4pystorage(data_dict): + """ + Cast dict of gt4py storage into dict of numpy arrays + """ + new_data_dict = {} + for key in data_dict: + data = data_dict[key] + if not isinstance(data, np.ndarray): data.synchronize() + new_data_dict[key] = data.view(np.ndarray) + + return new_data_dict diff --git a/projects2020/group05/submit_job.sh b/projects2020/group05/submit_job.sh new file mode 100644 index 00000000..c9cc4ace --- /dev/null +++ b/projects2020/group05/submit_job.sh @@ -0,0 +1,4 @@ +#!/bin/bash +sbatch -Cgpu -N 1 -t 60 -e slurm-numpy.err -o slurm-numpy.out -J 'gt4py-numpy-benchmark' --wrap="source ~/HPC4WC_project/env_daint;export GT4PY_BACKEND=numpy;ipython ~/HPC4WC_project/benchmark.py" +sbatch -Cgpu -N 1 -t 60 -e slurm-cuda.err -o slurm-cuda.out -J 'gt4py-cuda-benchmark' --wrap="source ~/HPC4WC_project/env_daint;export GT4PY_BACKEND=gtcuda;ipython ~/HPC4WC_project/benchmark.py" +sbatch -Cgpu -N 1 -t 60 -e slurm-x86.err -o slurm-x86.out -J 'gt4py-x86-benchmark' --wrap="source ~/HPC4WC_project/env_daint;export GT4PY_BACKEND=gtx86;ipython ~/HPC4WC_project/benchmark.py" diff --git a/projects2020/group05/tests/__init__.py b/projects2020/group05/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/projects2020/group05/tests/analyse_xml.py b/projects2020/group05/tests/analyse_xml.py new file mode 100644 index 00000000..5fd57f32 --- /dev/null +++ b/projects2020/group05/tests/analyse_xml.py @@ -0,0 +1,74 @@ +from lxml import etree + +#[i.tag for i in res[0]] #iterate children +#[i.tag for i in res[0].iter()] #iterate recursively +#[i.attrib for i in res[0].iter("assignment")] +# loop -> header #iterate var, upper/lower bound +# assignment -> [target, value] +# target -> name -> subscripts +# read lists: res[0].iter("name") +# temp var lists: only read on write in the same loop +# need to exclude subscript and assignment target from read lists +# selector.getpath(node) + + +def parse_xml(): + root = etree.parse("fortran/samfshalconv.xml") + return root + + +def visit_loop(node): + indexvars = node.xpath(".//index-variable") + indexvar_ids = [i.get("name") for i in indexvars] + writevars = node.xpath(".//assignment/target/name") + readvars = [i for i in node.xpath(".//*[not(name()='target')]/name") + if i.get("id") not in indexvar_ids] + + return indexvars, writevars, readvars + + +def find_vars_in_range(node, startlineno, endlineno): + writevars = [i for i in node.xpath(".//assignment/target/name") + if int(i.get("line_begin")) >= startlineno and int(i.get("line_begin")) <= endlineno] + readvars = [i for i in node.xpath(".//*[not(name()='target')]/name") + if int(i.get("line_begin")) >= startlineno and int(i.get("line_begin")) <= endlineno] + + return writevars, readvars + + +def get_declared_vars(root = parse_xml()): + return [i.get("name") for i in root.xpath(".//variables/variable")] + + +def get_declared_arr_vars(root = parse_xml()): + return [i.get("name") for i in root.xpath(".//variables/variable[dimensions]")] + + +def get_nonfloat_arr_vars(root = parse_xml()): + return [i.get("name") for i in + root.xpath(".//declaration[type[@name='integer' or @name='logical']]/variables/variable[dimensions]")] + + +def get_arguments_range(startlineno, endlineno): + root = parse_xml() + declaredarrvars = get_declared_arr_vars(root) + writevars, readvars = find_vars_in_range(root, startlineno, endlineno) + writevars = set([i.get("id") for i in writevars]) + readvars = set([i.get("id") for i in readvars + if i.get("id") not in ["n","i","k","min","max","fpvs","fpvsx"]]) + readvars_list = [i for i in declaredarrvars if i in readvars] + writevars_list = [i for i in declaredarrvars if i in writevars] + + return writevars_list, readvars_list, set(readvars_list).intersection(writevars_list) + + +def print_ser_def(varlist): + return " ".join([var+"="+var for var in varlist]) + + +#root = etree.parse("fortran/samfshalconv.xml") +#res = root.xpath('/ofp/file/subroutine/body/loop') +part1_range = (218, 458) +part2_range = (459, 1300) +part3_range = (1301, 1513) +part4_range = (1517, 1810) diff --git a/projects2020/group05/tests/fortran/.f2py_f2cmap b/projects2020/group05/tests/fortran/.f2py_f2cmap new file mode 100644 index 00000000..3df82859 --- /dev/null +++ b/projects2020/group05/tests/fortran/.f2py_f2cmap @@ -0,0 +1 @@ +{'real':{'kind_phys':'double','8':'double'}} \ No newline at end of file diff --git a/projects2020/group05/tests/fortran/part1.f90 b/projects2020/group05/tests/fortran/part1.f90 new file mode 100644 index 00000000..e5fb10d5 --- /dev/null +++ b/projects2020/group05/tests/fortran/part1.f90 @@ -0,0 +1,436 @@ +module mod + integer, parameter :: kind_phys = 8 + + real(kind=kind_phys),parameter:: grav =9.80665e+0_kind_phys + real(kind=kind_phys),parameter:: cp =1.0046e+3_kind_phys + real(kind=kind_phys),parameter:: hvap =2.5000e+6_kind_phys + real(kind=kind_phys),parameter:: rv =4.6150e+2_kind_phys + real(kind=kind_phys),parameter:: rd =2.8705e+2_kind_phys + real(kind=kind_phys),parameter:: fv =rv/rd-1. + real(kind=kind_phys),parameter:: t0c =2.7315e+2_kind_phys + real(kind=kind_phys),parameter:: cvap =1.8460e+3_kind_phys + real(kind=kind_phys),parameter:: cliq =4.1855e+3_kind_phys + real(kind=kind_phys),parameter:: eps =rd/rv + real(kind=kind_phys),parameter:: epsm1 =rd/rv-1. + + real(kind=kind_phys),parameter:: ttp =2.7316e+2_kind_phys + real(kind=kind_phys),parameter:: csol =2.1060e+3_kind_phys + real(kind=kind_phys),parameter:: hfus =3.3358e+5_kind_phys + real(kind=kind_phys),parameter:: psat =6.1078e+2_kind_phys +contains +elemental function fpvsx(t) + implicit none + integer, parameter :: kind_phys = 8 + real(kind=kind_phys) fpvsx + real(kind=kind_phys),intent(in):: t + real(kind=kind_phys),parameter:: tliq=ttp + real(kind=kind_phys),parameter:: tice=ttp-20.0 + real(kind=kind_phys),parameter:: dldtl=cvap-cliq + real(kind=kind_phys),parameter:: heatl=hvap + real(kind=kind_phys),parameter:: xponal=-dldtl/rv + real(kind=kind_phys),parameter:: xponbl=-dldtl/rv+heatl/(rv*ttp) + real(kind=kind_phys),parameter:: dldti=cvap-csol + real(kind=kind_phys),parameter:: heati=hvap+hfus + real(kind=kind_phys),parameter:: xponai=-dldti/rv + real(kind=kind_phys),parameter:: xponbi=-dldti/rv+heati/(rv*ttp) + real(kind=kind_phys) tr,w,pvl,pvi +! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + tr=ttp/t + if(t.ge.tliq) then + fpvsx=psat*(tr**xponal)*exp(xponbl*(1.-tr)) + elseif(t.lt.tice) then + fpvsx=psat*(tr**xponai)*exp(xponbi*(1.-tr)) + else + w=(t-tice)/(tliq-tice) + pvl=psat*(tr**xponal)*exp(xponbl*(1.-tr)) + pvi=psat*(tr**xponai)*exp(xponbi*(1.-tr)) + fpvsx=w*pvl+(1.-w)*pvi + endif +! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +end function + +subroutine part1(im,ix,km,delt,itc,ntc,ntk,ntr,delp, & + & prslp,psp,phil,qtr,q1,t1,u1,v1,fscav, & + & rn,kbot,ktop,kcnv,islimsk,garea, & + & dot,ncloud,hpbl,ud_mf,dt_mf,cnvw,cnvc, & + & clam,c0s,c1,pgcon,asolfac, & + & heo, heso, qo, qeso) + implicit none +! + integer, parameter :: kind_phys = 8 + integer, intent(in) :: im, ix, km, itc, ntc, ntk, ntr, ncloud + integer, intent(in) :: islimsk(im) + real(kind=kind_phys), intent(in) :: delt + real(kind=kind_phys), intent(in) :: psp(im), delp(ix,km), & + & prslp(ix,km), garea(im), hpbl(im), dot(ix,km), phil(ix,km) +! + real(kind=kind_phys), intent(in) :: fscav(ntc) + integer, intent(in) :: kcnv(im) + real(kind=kind_phys), intent(in) :: qtr(ix,km,ntr+2), & + & q1(ix,km), t1(ix,km), u1(ix,km), v1(ix,km) +! + integer :: kbot(im), ktop(im) + real(kind=kind_phys) :: rn(im), & + & cnvw(ix,km), cnvc(ix,km), ud_mf(im,km), dt_mf(im,km) +! + real(kind=kind_phys), intent(in) :: clam, c0s, c1, & + & asolfac, pgcon +! +! local variables + integer i,j,indx, k, kk, km1, n + integer kpbl(im) +! + real(kind=kind_phys) clamd, tkemx, tkemn, dtke +! + real(kind=kind_phys) dellat, delta, & + & c0l, d0, & + & desdt, dp, & + & dq, dqsdp, dqsdt, dt, & + & dt2, dtmax, dtmin, dxcrt, & + & dv1h, dv2h, dv3h, & + & dv1q, dv2q, dv3q, & + & dz, dz1, e1, & + & el2orc, elocp, aafac, cm, & + & es, etah, h1, & + & evef, evfact, evfactl, fact1, & + & fact2, factor, dthk, & + & g, gamma, pprime, betaw, & + & qlk, qrch, qs, & + & rfact, shear, tfac, & + & val, val1, val2, & + & w1, w1l, w1s, w2, & + & w2l, w2s, w3, w3l, & + & w3s, w4, w4l, w4s, & + & rho, tem, tem1, tem2, & + & ptem, ptem1 +! + integer kb(im), kbcon(im), kbcon1(im), & + & ktcon(im), ktcon1(im), ktconn(im), & + & kbm(im), kmax(im) +! + real(kind=kind_phys) aa1(im), cina(im), & + & tkemean(im), clamt(im), & + & ps(im), del(ix,km), prsl(ix,km), & + & umean(im), tauadv(im), gdx(im), & + & delhbar(im), delq(im), delq2(im), & + & delqbar(im), delqev(im), deltbar(im), & + & deltv(im), dtconv(im), edt(im), & + & pdot(im), po(im,km), & + & qcond(im), qevap(im), hmax(im), & + & rntot(im), vshear(im), & + & xlamud(im), xmb(im), xmbmax(im), & + & delebar(im,ntr), & + & delubar(im), delvbar(im) +! + real(kind=kind_phys) c0(im) +! + real(kind=kind_phys) crtlamd +! + real(kind=kind_phys) cinpcr, cinpcrmx, cinpcrmn, & + & cinacr, cinacrmx, cinacrmn +! +! parameters for updraft velocity calculation + real(kind=kind_phys) bet1, cd1, f1, gam1, & + & bb1, bb2 +! & bb1, bb2, wucb +!c +! physical parameters +! parameter(g=grav,asolfac=0.89) + parameter(g=grav) + parameter(elocp=hvap/cp, & + & el2orc=hvap*hvap/(rv*cp)) +! parameter(c0s=0.002,c1=5.e-4,d0=.01) +! parameter(d0=.01) + parameter(d0=.001) +! parameter(c0l=c0s*asolfac) +! +! asolfac: aerosol-aware parameter based on Lim & Hong (2012) +! asolfac= cx / c0s(=.002) +! cx = min([-0.7 ln(Nccn) + 24]*1.e-4, c0s) +! Nccn: CCN number concentration in cm^(-3) +! Until a realistic Nccn is provided, Nccns are assumed +! as Nccn=100 for sea and Nccn=1000 for land +! + parameter(cm=1.0,delta=fv) + parameter(fact1=(cvap-cliq)/rv,fact2=hvap/rv-fact1*t0c) + parameter(clamd=0.1,tkemx=0.65,tkemn=0.05) + parameter(dtke=tkemx-tkemn) + parameter(dthk=25.) + parameter(cinpcrmx=180.,cinpcrmn=120.) +! parameter(cinacrmx=-120.,cinacrmn=-120.) + parameter(cinacrmx=-120.,cinacrmn=-80.) + parameter(crtlamd=3.e-4) + parameter(dtmax=10800.,dtmin=600.) + parameter(bet1=1.875,cd1=.506,f1=2.0,gam1=.5) + parameter(betaw=.03,dxcrt=15.e3) + parameter(h1=0.33333333) +! local variables and arrays + real(kind=kind_phys) pfld(im,km), to(im,km), & !qo(im,km), & + & uo(im,km), vo(im,km), & !qeso(im,km), & + & ctr(im,km,ntr), ctro(im,km,ntr) +! for aerosol transport + real(kind=kind_phys) qaero(im,km,ntc) +! for updraft velocity calculation + real(kind=kind_phys) wu2(im,km), buo(im,km), drag(im,km) + real(kind=kind_phys) wc(im), scaldfunc(im), sigmagfm(im) +! +! cloud water +! real(kind=kind_phys) qlko_ktcon(im), dellal(im,km), tvo(im,km), + real(kind=kind_phys) qlko_ktcon(im), dellal(im,km), & + & dbyo(im,km), zo(im,km), xlamue(im,km), & +! & heo(im,km), heso(im,km), & + & dellah(im,km), dellaq(im,km), & + & dellae(im,km,ntr), & + & dellau(im,km), dellav(im,km), hcko(im,km), & + & ucko(im,km), vcko(im,km), qcko(im,km), & + & qrcko(im,km), ecko(im,km,ntr), & + & eta(im,km), & + & zi(im,km), pwo(im,km), c0t(im,km), & + & sumx(im), tx1(im), cnvwt(im,km) +! + logical do_aerosols, totflg, cnvflg(im), flg(im) + real(kind=kind_phys), intent(out) :: heo(im,km), heso(im,km), qo(im,km), qeso(im,km) +! + real(kind=kind_phys) tf, tcr, tcrf + parameter (tf=233.16, tcr=263.16, tcrf=1.0/(tcr-tf)) +! +!----------------------------------------------------------------------- +!> ## Determine whether to perform aerosol transport + do_aerosols = (itc > 0) .and. (ntc > 0) .and. (ntr > 0) + if (do_aerosols) do_aerosols = (ntr >= itc + ntc - 3) +! +!************************************************************************ +! convert input Pa terms to Cb terms -- Moorthi +!> ## Compute preliminary quantities needed for the static and feedback control portions of the algorithm. +!> - Convert input pressure terms to centibar units. + ps = psp * 0.001 + prsl = prslp * 0.001 + del = delp * 0.001 +!************************************************************************ +! + km1 = km - 1 +! +! initialize arrays +! +!> - Initialize column-integrated and other single-value-per-column variable arrays. + do i=1,im + cnvflg(i) = .true. + if(kcnv(i) == 1) cnvflg(i) = .false. + if(cnvflg(i)) then + kbot(i)=km+1 + ktop(i)=0 + endif + rn(i)=0. + kbcon(i)=km + ktcon(i)=1 + ktconn(i)=1 + kb(i)=km + pdot(i) = 0. + qlko_ktcon(i) = 0. + edt(i) = 0. + aa1(i) = 0. + cina(i) = 0. + vshear(i) = 0. + gdx(i) = sqrt(garea(i)) + enddo +!! +!> - Return to the calling routine if deep convection is present or the surface buoyancy flux is negative. + totflg = .true. + do i=1,im + totflg = totflg .and. (.not. cnvflg(i)) + enddo + if(totflg) return +!! +!> - determine aerosol-aware rain conversion parameter over land + do i=1,im + if(islimsk(i) == 1) then + c0(i) = c0s*asolfac + else + c0(i) = c0s + endif + enddo +! +!> - determine rain conversion parameter above the freezing level which exponentially decreases with decreasing temperature from Han et al.'s (2017) \cite han_et_al_2017 equation 8. + do k = 1, km + do i = 1, im + if(t1(i,k) > 273.16) then + c0t(i,k) = c0(i) + else + tem = d0 * (t1(i,k) - 273.16) + tem1 = exp(tem) + c0t(i,k) = c0(i) * tem1 + endif + enddo + enddo +! +!> - Initialize convective cloud water and cloud cover to zero. + do k = 1, km + do i = 1, im + cnvw(i,k) = 0. + cnvc(i,k) = 0. + enddo + enddo +! hchuang code change +!> - Initialize updraft mass fluxes to zero. + do k = 1, km + do i = 1, im + ud_mf(i,k) = 0. + dt_mf(i,k) = 0. + enddo + enddo +! + dt2 = delt +! +! model tunable parameters are all here +! clam = .3 +! aafac = .1 + aafac = .05 +! evef = 0.07 + evfact = 0.3 + evfactl = 0.3 +! +! pgcon = 0.7 ! Gregory et al. (1997, QJRMS) +! pgcon = 0.55 ! Zhang & Wu (2003,JAS) + w1l = -8.e-3 + w2l = -4.e-2 + w3l = -5.e-3 + w4l = -5.e-4 + w1s = -2.e-4 + w2s = -2.e-3 + w3s = -1.e-3 + w4s = -2.e-5 +! +! define top layer for search of the downdraft originating layer +! and the maximum thetae for updraft +! +!> - Determine maximum indices for the parcel starting point (kbm) and cloud top (kmax). + do i=1,im + kbm(i) = km + kmax(i) = km + tx1(i) = 1.0 / ps(i) + enddo +! + do k = 1, km + do i=1,im + if (prsl(i,k)*tx1(i) > 0.70) kbm(i) = k + 1 + if (prsl(i,k)*tx1(i) > 0.60) kmax(i) = k + 1 + enddo + enddo + do i=1,im + kbm(i) = min(kbm(i),kmax(i)) + enddo +! +! hydrostatic height assume zero terr and compute +! updraft entrainment rate as an inverse function of height +! +!> - Calculate hydrostatic height at layer centers assuming a flat surface (no terrain) from the geopotential. + do k = 1, km + do i=1,im + zo(i,k) = phil(i,k) / g + enddo + enddo +!> - Calculate interface height + do k = 1, km1 + do i=1,im + zi(i,k) = 0.5*(zo(i,k)+zo(i,k+1)) + enddo + enddo +! +! pbl height +! +!> - Find the index for the PBL top using the PBL height; enforce that it is lower than the maximum parcel starting level. + do i=1,im + flg(i) = cnvflg(i) + kpbl(i)= 1 + enddo + do k = 2, km1 + do i=1,im + if (flg(i) .and. zo(i,k) <= hpbl(i)) then + kpbl(i) = k + else + flg(i) = .false. + endif + enddo + enddo + do i=1,im + kpbl(i)= min(kpbl(i),kbm(i)) + enddo +! +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +! convert surface pressure to mb from cb +! +!> - Convert prsl from centibar to millibar, set normalized mass flux to 1, cloud properties to 0, and save model state variables (after advection/turbulence). + do k = 1, km + do i = 1, im + if (cnvflg(i) .and. k <= kmax(i)) then + pfld(i,k) = prsl(i,k) * 10.0 + eta(i,k) = 1. + hcko(i,k) = 0. + qcko(i,k) = 0. + qrcko(i,k)= 0. + ucko(i,k) = 0. + vcko(i,k) = 0. + dbyo(i,k) = 0. + pwo(i,k) = 0. + dellal(i,k) = 0. + to(i,k) = t1(i,k) + qo(i,k) = q1(i,k) + uo(i,k) = u1(i,k) + vo(i,k) = v1(i,k) +! uo(i,k) = u1(i,k) * rcs(i) +! vo(i,k) = v1(i,k) * rcs(i) + wu2(i,k) = 0. + buo(i,k) = 0. + drag(i,k) = 0. + cnvwt(i,k) = 0. + endif + enddo + enddo +! +! initialize tracer variables +! + do n = 3, ntr+2 + kk = n-2 + do k = 1, km + do i = 1, im + if (cnvflg(i) .and. k <= kmax(i)) then + ctr(i,k,kk) = qtr(i,k,n) + ctro(i,k,kk) = qtr(i,k,n) + ecko(i,k,kk) = 0. + endif + enddo + enddo + enddo +!> - Calculate saturation specific humidity and enforce minimum moisture values. + do k = 1, km + do i=1,im + if (cnvflg(i) .and. k <= kmax(i)) then + qeso(i,k) = 0.01 * fpvsx(to(i,k)) ! fpvs is in pa + qeso(i,k) = eps * qeso(i,k) / (pfld(i,k) + epsm1*qeso(i,k)) + val1 = 1.e-8 + qeso(i,k) = max(qeso(i,k), val1) + val2 = 1.e-10 + qo(i,k) = max(qo(i,k), val2 ) +! qo(i,k) = min(qo(i,k),qeso(i,k)) +! tvo(i,k) = to(i,k) + delta * to(i,k) * qo(i,k) + endif + enddo + enddo +! +! compute moist static energy +! +!> - Calculate moist static energy (heo) and saturation moist static energy (heso). + do k = 1, km + do i=1,im + if (cnvflg(i) .and. k <= kmax(i)) then +! tem = g * zo(i,k) + cp * to(i,k) + tem = phil(i,k) + cp * to(i,k) + heo(i,k) = tem + hvap * qo(i,k) + heso(i,k) = tem + hvap * qeso(i,k) +! heo(i,k) = min(heo(i,k),heso(i,k)) + endif + enddo + enddo +end subroutine part1 + +end module mod \ No newline at end of file diff --git a/projects2020/group05/tests/fortran/part2.f90 b/projects2020/group05/tests/fortran/part2.f90 new file mode 100644 index 00000000..e73bd4f6 --- /dev/null +++ b/projects2020/group05/tests/fortran/part2.f90 @@ -0,0 +1,135 @@ +module mod + ! PARAMETERS + integer, parameter :: kind_phys = 8 + real(kind=kind_phys),parameter:: grav =9.80665e+0_kind_phys + real(kind=kind_phys),parameter:: cp =1.0046e+3_kind_phys + real(kind=kind_phys),parameter:: hvap =2.5000e+6_kind_phys + real(kind=kind_phys),parameter:: rv =4.6150e+2_kind_phys + real(kind=kind_phys),parameter:: rd =2.8705e+2_kind_phys + real(kind=kind_phys),parameter:: fv =rv/rd-1. + real(kind=kind_phys),parameter:: t0c =2.7315e+2_kind_phys + real(kind=kind_phys),parameter:: cvap =1.8460e+3_kind_phys + real(kind=kind_phys),parameter:: cliq =4.1855e+3_kind_phys + real(kind=kind_phys),parameter:: eps =rd/rv + real(kind=kind_phys),parameter:: epsm1 =rd/rv-1. + + real(kind=kind_phys),parameter:: ttp =2.7316e+2_kind_phys + real(kind=kind_phys),parameter:: csol =2.1060e+3_kind_phys + real(kind=kind_phys),parameter:: hfus =3.3358e+5_kind_phys + real(kind=kind_phys),parameter:: psat =6.1078e+2_kind_phys + ! INPUT VALUES + integer im, ix, km + real(kind=kind_phys) clam, c0s, c1, asolfac, pgcon + ! INTERMEDIATE ARRAYS + logical cnvflg(ix) + integer kb(im,km), kmax(im,km) + real(kind=kind_phys) heo(im,km), heso(im,km) + real(kind=kind_phys) zi(im,km), xlamue(im,km), xlamud(im) + real(kind=kind_phys) hcko(im,km), dbyo(im,km), ucko(im,km), vcko(im,km) +contains +elemental function fpvsx(t) + implicit none + integer, parameter :: kind_phys = 8 + real(kind=kind_phys) fpvsx + real(kind=kind_phys),intent(in):: t + real(kind=kind_phys),parameter:: tliq=ttp + real(kind=kind_phys),parameter:: tice=ttp-20.0 + real(kind=kind_phys),parameter:: dldtl=cvap-cliq + real(kind=kind_phys),parameter:: heatl=hvap + real(kind=kind_phys),parameter:: xponal=-dldtl/rv + real(kind=kind_phys),parameter:: xponbl=-dldtl/rv+heatl/(rv*ttp) + real(kind=kind_phys),parameter:: dldti=cvap-csol + real(kind=kind_phys),parameter:: heati=hvap+hfus + real(kind=kind_phys),parameter:: xponai=-dldti/rv + real(kind=kind_phys),parameter:: xponbi=-dldti/rv+heati/(rv*ttp) + real(kind=kind_phys) tr,w,pvl,pvi + ! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + tr=ttp/t + if(t.ge.tliq) then + fpvsx=psat*(tr**xponal)*exp(xponbl*(1.-tr)) + elseif(t.lt.tice) then + fpvsx=psat*(tr**xponai)*exp(xponbi*(1.-tr)) + else + w=(t-tice)/(tliq-tice) + pvl=psat*(tr**xponal)*exp(xponbl*(1.-tr)) + pvi=psat*(tr**xponai)*exp(xponbi*(1.-tr)) + fpvsx=w*pvl+(1.-w)*pvi + endif +! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +end function + +subroutine part2_init(im_v, ix_v, km_v, clam_v, c0s_v, c1_v, asolfac_v, pgcon_v, & + &cnvflg_v, kb_v, kmax_v, heo_v, heso_v, zi_v, xlamue_v, xlamud_v, & + &hcko_v, dbyo_v, ucko_v, vcko_v) + implicit none + integer, intent(in):: im_v, ix_v, km_v + real(kind=kind_phys), intent(in):: clam_v, c0s_v, c1_v, asolfac_v, pgcon_v + ! INTERMEDIATE ARRAYS + logical, intent(in):: cnvflg_v(ix_v) + integer, intent(in):: kb_v(im_v,km_v), kmax_v(im_v,km_v) + real(kind=kind_phys), intent(in):: heo_v(im_v,km_v), heso_v(im_v,km_v) + real(kind=kind_phys), intent(in):: zi_v(im_v,km_v), xlamue_v(im_v,km_v), xlamud_v(im_v) + real(kind=kind_phys), intent(in):: hcko_v(im_v,km_v), dbyo_v(im_v,km_v), ucko_v(im_v,km_v), vcko_v(im_v,km_v) + im = im_v + ix = ix_v + km = km_v + clam = clam_v + c0s = c0s_v + c1 = c1_v + asolfac = asolfac_v + pgcon = pgcon_v + do i = 1, im + cnvflg(i) = cnvflg_v(i) + xlamud(i) = xlamud_v(i) + do k = 1, km + kb(i,k) = kb_v(i,k) + kmax(i,k) = kmax_v(i,k) + heo(i,k) = heo_v(i,k) + heso(i,k) = heso_v(i,k) + zi(i,k) = zi_v(i,k) + xlamue(i,k) = xlamue_v(i,k) + hcko(i,k) = hcko_v(i,k) + dbyo(i,k) = dbyo_v(i,k) + ucko(i,k) = ucko_v(i,k) + vcko(i,k) = vcko_v(i,k) + end do + end do + + + +end subroutine part2_init + +subroutine part2_stencil7_line756(hcko, dbyo, ucko, vcko) + implicit none + integer km1 + km1 = km - 1 +! +! cm is an enhancement factor in entrainment rates for momentum +! +!> - Calculate the cloud properties as a parcel ascends, modified by entrainment and detrainment. Discretization follows Appendix B of Grell (1993) \cite grell_1993 . Following Han and Pan (2006) \cite han_and_pan_2006, the convective momentum transport is reduced by the convection-induced pressure gradient force by the constant "pgcon", currently set to 0.55 after Zhang and Wu (2003) \cite zhang_and_wu_2003 . + do k = 2, km1 + do i = 1, im + if (cnvflg(i)) then + if(k > kb(i) .and. k < kmax(i)) then + dz = zi(i,k) - zi(i,k-1) + tem = 0.5 * (xlamue(i,k)+xlamue(i,k-1)) * dz + tem1 = 0.5 * xlamud(i) * dz + factor = 1. + tem - tem1 + hcko(i,k) = ((1.-tem1)*hcko(i,k-1)+tem*0.5* & + & (heo(i,k)+heo(i,k-1)))/factor + dbyo(i,k) = hcko(i,k) - heso(i,k) +! + tem = 0.5 * cm * tem + factor = 1. + tem + ptem = tem + pgcon + ptem1= tem - pgcon + ucko(i,k) = ((1.-tem)*ucko(i,k-1)+ptem*uo(i,k) & + & +ptem1*uo(i,k-1))/factor + vcko(i,k) = ((1.-tem)*vcko(i,k-1)+ptem*vo(i,k) & + & +ptem1*vo(i,k-1))/factor + endif + endif + enddo + enddo +end subroutine part2_stencil7_line756 +end module mod \ No newline at end of file diff --git a/projects2020/group05/tests/fortran/samfshalconv.f90 b/projects2020/group05/tests/fortran/samfshalconv.f90 new file mode 100644 index 00000000..2336ded9 --- /dev/null +++ b/projects2020/group05/tests/fortran/samfshalconv.f90 @@ -0,0 +1,1809 @@ +!> \defgroup SAMF_shal Scale-Aware Mass-Flux Shallow Convection +!! @{ +!! \brief The scale-aware mass-flux shallow (SAMF_shal) convection scheme is an updated version of the previous mass-flux shallow convection scheme with scale and aerosol awareness and parameterizes the effect of shallow convection on the environment. The SAMF_shal scheme is similar to the SAMF deep convection scheme but with a few key differences. First, no quasi-equilibrium assumption is used for any grid size and the shallow cloud base mass flux is parameterized using a mean updraft velocity. Further, there are no convective downdrafts, the entrainment rate is greater than for deep convection, and the shallow convection is limited to not extend over the level where \f$p=0.7p_{sfc}\f$. The paramerization of scale and aerosol awareness follows that of the SAMF deep convection scheme. +!! +!! The previous version of the shallow convection scheme (shalcnv.f) is described in Han and Pan (2011) \cite han_and_pan_2011 and differences between the shallow and deep convection schemes are presented in Han and Pan (2011) \cite han_and_pan_2011 and Han et al. (2017) \cite han_et_al_2017 . Details of scale- and aerosol-aware parameterizations are described in Han et al. (2017) \cite han_et_al_2017 . +!! +!! In further update for FY19 GFS implementation, interaction with turbulent kinetic energy (TKE), which is a prognostic variable used in a scale-aware TKE-based moist EDMF vertical turbulent mixing scheme, is included. Entrainment rates in updrafts are proportional to sub-cloud mean TKE. TKE is transported by cumulus convection. TKE contribution from cumulus convection is deduced from cumulus mass flux. On the other hand, tracers such as ozone and aerosol are also transported by cumulus convection. +!! +!! To reduce too much convective cooling at the cloud top, the convection schemes have been modified for the rain conversion rate, entrainment and detrainment rates, overshooting layers, and maximum allowable cloudbase mass flux (as of June 2018). +!! +!! \section diagram Calling Hierarchy Diagram +!! \image html SAMF_shal_Flowchart.png "Diagram depicting how the SAMF shallow convection scheme is called from the FV3GFS physics time loop" height=2cm +!! \section intraphysics Intraphysics Communication +!! This space is reserved for a description of how this scheme uses information from other scheme types and/or how information calculated in this scheme is used in other scheme types. + +!> \file samfshalcnv.f +!! Contains the entire SAMF shallow convection scheme. + +!> \brief This subroutine contains the entirety of the SAMF shallow convection scheme. +!! +!! This routine follows the \ref SAMF deep scheme quite closely, although it can be interpreted as only having the "static" and "feedback" control portions, since the "dynamic" control is not necessary to find the cloud base mass flux. The algorithm is simplified from SAMF deep convection by excluding convective downdrafts and being confined to operate below \f$p=0.7p_{sfc}\f$. Also, entrainment is both simpler and stronger in magnitude compared to the deep scheme. +!! +!! \param[in] im number of used points +!! \param[in] ix horizontal dimension +!! \param[in] km vertical layer dimension +!! \param[in] delt physics time step in seconds +!! \param[in] ntk index for TKE +!! \param[in] ntr total number of tracers including TKE +!! \param[in] delp pressure difference between level k and k+1 (Pa) +!! \param[in] prslp mean layer presure (Pa) +!! \param[in] psp surface pressure (Pa) +!! \param[in] phil layer geopotential (\f$m^s/s^2\f$) +!! \param[in] qtr tracer array including cloud condensate (\f$kg/kg\f$) +!! \param[inout] ql cloud water or ice (kg/kg) +!! \param[inout] q1 updated tracers (kg/kg) +!! \param[inout] t1 updated temperature (K) +!! \param[inout] u1 updated zonal wind (\f$m s^{-1}\f$) +!! \param[inout] v1 updated meridional wind (\f$m s^{-1}\f$) +!! \param[out] rn convective rain (m) +!! \param[out] kbot index for cloud base +!! \param[out] ktop index for cloud top +!! \param[out] kcnv flag to denote deep convection (0=no, 1=yes) +!! \param[in] islimsk sea/land/ice mask (=0/1/2) +!! \param[in] dot layer mean vertical velocity (Pa/s) +!! \param[in] ncloud number of cloud species +!! \param[in] hpbl PBL height (m) +!! \param[in] heat surface sensible heat flux (K m/s) +!! \param[in] evap surface latent heat flux (kg/kg m/s) +!! \param[out] ud_mf updraft mass flux multiplied by time step (\f$kg/m^2\f$) +!! \param[out] dt_mf ud_mf at cloud top (\f$kg/m^2\f$) +!! \param[out] cnvw convective cloud water (kg/kg) +!! \param[out] cnvc convective cloud cover (unitless) +!! \param[in] clam coefficient for entrainment rate +!! \param[in] c0s convective rain conversion parameter (1/m) +!! \param[in] c1 conversion parameter of detrainment from liquid water into grid-scale cloud water (1/m) +!! \param[in] pgcon reduction factor in momentum transport due to convection induced pressure gradient force +!! \param[in] asolfac aerosol-aware parameter inversely proportional to CCN number concentraion +!! +!! \section general General Algorithm +!! -# Compute preliminary quantities needed for the static and feedback control portions of the algorithm. +!! -# Perform calculations related to the updraft of the entraining/detraining cloud model ("static control"). +!! -# The cloud base mass flux is obtained using the cumulus updraft velocity averaged ove the whole cloud depth. +!! -# Calculate the tendencies of the state variables (per unit cloud base mass flux) and the cloud base mass flux. +!! -# For the "feedback control", calculate updated values of the state variables by multiplying the cloud base mass flux and the tendencies calculated per unit cloud base mass flux from the static control. +!! \section detailed Detailed Algorithm +!! @{ +! subroutine samfshalcnv(im,ix,km,delt,ntk,ntr,delp, + subroutine samfshalcnv(im,ix,km,delt,itc,ntc,ntk,ntr,delp, & + & prslp,psp,phil,qtr,q1,t1,u1,v1,fscav, & + & rn,kbot,ktop,kcnv,islimsk,garea, & + & dot,ncloud,hpbl,ud_mf,dt_mf,cnvw,cnvc, & + & clam,c0s,c1,pgcon,asolfac) +! + use machine , only : kind_phys + use funcphys , only : fpvs => fpvsx + use physcons, grav => con_g, cp => con_cp, hvap => con_hvap & + &, rv => con_rv, fv => con_fvirt, t0c => con_t0c & + &, rd => con_rd, cvap => con_cvap, cliq => con_cliq & + &, eps => con_eps, epsm1 => con_epsm1 + implicit none +! + integer, intent(in) :: im, ix, km, itc, ntc, ntk, ntr, ncloud + integer, intent(in) :: islimsk(im) + real(kind=kind_phys), intent(in) :: delt + real(kind=kind_phys), intent(in) :: psp(im), delp(ix,km), & + & prslp(ix,km), garea(im), hpbl(im), dot(ix,km), phil(ix,km) +! + real(kind=kind_phys), intent(in) :: fscav(ntc) + integer, intent(inout) :: kcnv(im) + real(kind=kind_phys), intent(inout) :: qtr(ix,km,ntr+2), & + & q1(ix,km), t1(ix,km), u1(ix,km), v1(ix,km) +! + integer, intent(out) :: kbot(im), ktop(im) + real(kind=kind_phys), intent(out) :: rn(im), & + & cnvw(ix,km), cnvc(ix,km), ud_mf(im,km), dt_mf(im,km) +! + real(kind=kind_phys), intent(in) :: clam, c0s, c1, & + & asolfac, pgcon +! +! local variables + integer i,j,indx, k, kk, km1, n + integer kpbl(im) +! + real(kind=kind_phys) clamd, tkemx, tkemn, dtke +! + real(kind=kind_phys) dellat, delta, & + & c0l, d0, & + & desdt, dp, & + & dq, dqsdp, dqsdt, dt, & + & dt2, dtmax, dtmin, dxcrt, & + & dv1h, dv2h, dv3h, & + & dv1q, dv2q, dv3q, & + & dz, dz1, e1, & + & el2orc, elocp, aafac, cm, & + & es, etah, h1, & + & evef, evfact, evfactl, fact1, & + & fact2, factor, dthk, & + & g, gamma, pprime, betaw, & + & qlk, qrch, qs, & + & rfact, shear, tfac, & + & val, val1, val2, & + & w1, w1l, w1s, w2, & + & w2l, w2s, w3, w3l, & + & w3s, w4, w4l, w4s, & + & rho, tem, tem1, tem2, & + & ptem, ptem1 +! + integer kb(im), kbcon(im), kbcon1(im), & + & ktcon(im), ktcon1(im), ktconn(im), & + & kbm(im), kmax(im) +! + real(kind=kind_phys) aa1(im), cina(im), & + & tkemean(im), clamt(im), & + & ps(im), del(ix,km), prsl(ix,km), & + & umean(im), tauadv(im), gdx(im), & + & delhbar(im), delq(im), delq2(im), & + & delqbar(im), delqev(im), deltbar(im), & + & deltv(im), dtconv(im), edt(im), & + & pdot(im), po(im,km), & + & qcond(im), qevap(im), hmax(im), & + & rntot(im), vshear(im), & + & xlamud(im), xmb(im), xmbmax(im), & + & delebar(im,ntr), & + & delubar(im), delvbar(im) +! + real(kind=kind_phys) c0(im) +! + real(kind=kind_phys) crtlamd +! + real(kind=kind_phys) cinpcr, cinpcrmx, cinpcrmn, & + & cinacr, cinacrmx, cinacrmn +! +! parameters for updraft velocity calculation + real(kind=kind_phys) bet1, cd1, f1, gam1, & + & bb1, bb2 +! & bb1, bb2, wucb +!c +! physical parameters +! parameter(g=grav,asolfac=0.89) + parameter(g=grav) + parameter(elocp=hvap/cp, & + & el2orc=hvap*hvap/(rv*cp)) +! parameter(c0s=0.002,c1=5.e-4,d0=.01) +! parameter(d0=.01) + parameter(d0=.001) +! parameter(c0l=c0s*asolfac) +! +! asolfac: aerosol-aware parameter based on Lim & Hong (2012) +! asolfac= cx / c0s(=.002) +! cx = min([-0.7 ln(Nccn) + 24]*1.e-4, c0s) +! Nccn: CCN number concentration in cm^(-3) +! Until a realistic Nccn is provided, Nccns are assumed +! as Nccn=100 for sea and Nccn=1000 for land +! + parameter(cm=1.0,delta=fv) + parameter(fact1=(cvap-cliq)/rv,fact2=hvap/rv-fact1*t0c) + parameter(clamd=0.1,tkemx=0.65,tkemn=0.05) + parameter(dtke=tkemx-tkemn) + parameter(dthk=25.) + parameter(cinpcrmx=180.,cinpcrmn=120.) +! parameter(cinacrmx=-120.,cinacrmn=-120.) + parameter(cinacrmx=-120.,cinacrmn=-80.) + parameter(crtlamd=3.e-4) + parameter(dtmax=10800.,dtmin=600.) + parameter(bet1=1.875,cd1=.506,f1=2.0,gam1=.5) + parameter(betaw=.03,dxcrt=15.e3) + parameter(h1=0.33333333) +! local variables and arrays + real(kind=kind_phys) pfld(im,km), to(im,km), qo(im,km), & + & uo(im,km), vo(im,km), qeso(im,km), & + & ctr(im,km,ntr), ctro(im,km,ntr) +! for aerosol transport + real(kind=kind_phys) qaero(im,km,ntc) +! for updraft velocity calculation + real(kind=kind_phys) wu2(im,km), buo(im,km), drag(im,km) + real(kind=kind_phys) wc(im), scaldfunc(im), sigmagfm(im) +! +! cloud water +! real(kind=kind_phys) qlko_ktcon(im), dellal(im,km), tvo(im,km), + real(kind=kind_phys) qlko_ktcon(im), dellal(im,km), & + & dbyo(im,km), zo(im,km), xlamue(im,km), & + & heo(im,km), heso(im,km), & + & dellah(im,km), dellaq(im,km), & + & dellae(im,km,ntr), & + & dellau(im,km), dellav(im,km), hcko(im,km), & + & ucko(im,km), vcko(im,km), qcko(im,km), & + & qrcko(im,km), ecko(im,km,ntr), & + & eta(im,km), & + & zi(im,km), pwo(im,km), c0t(im,km), & + & sumx(im), tx1(im), cnvwt(im,km) +! + logical do_aerosols, totflg, cnvflg(im), flg(im) +! + real(kind=kind_phys) tf, tcr, tcrf + parameter (tf=233.16, tcr=263.16, tcrf=1.0/(tcr-tf)) +! +!----------------------------------------------------------------------- +!> ## Determine whether to perform aerosol transport + do_aerosols = (itc > 0) .and. (ntc > 0) .and. (ntr > 0) + if (do_aerosols) do_aerosols = (ntr >= itc + ntc - 3) +! +!************************************************************************ +! convert input Pa terms to Cb terms -- Moorthi +!> ## Compute preliminary quantities needed for the static and feedback control portions of the algorithm. +!> - Convert input pressure terms to centibar units. + ps = psp * 0.001 + prsl = prslp * 0.001 + del = delp * 0.001 +!************************************************************************ +! + km1 = km - 1 +! +! initialize arrays +! +!> - Initialize column-integrated and other single-value-per-column variable arrays. + do i=1,im + cnvflg(i) = .true. + if(kcnv(i) == 1) cnvflg(i) = .false. + if(cnvflg(i)) then + kbot(i)=km+1 + ktop(i)=0 + endif + rn(i)=0. + kbcon(i)=km + ktcon(i)=1 + ktconn(i)=1 + kb(i)=km + pdot(i) = 0. + qlko_ktcon(i) = 0. + edt(i) = 0. + aa1(i) = 0. + cina(i) = 0. + vshear(i) = 0. + gdx(i) = sqrt(garea(i)) + enddo +!! +!> - Return to the calling routine if deep convection is present or the surface buoyancy flux is negative. + totflg = .true. + do i=1,im + totflg = totflg .and. (.not. cnvflg(i)) + enddo + if(totflg) return +!! +!> - determine aerosol-aware rain conversion parameter over land + do i=1,im + if(islimsk(i) == 1) then + c0(i) = c0s*asolfac + else + c0(i) = c0s + endif + enddo +! +!> - determine rain conversion parameter above the freezing level which exponentially decreases with decreasing temperature from Han et al.'s (2017) \cite han_et_al_2017 equation 8. + do k = 1, km + do i = 1, im + if(t1(i,k) > 273.16) then + c0t(i,k) = c0(i) + else + tem = d0 * (t1(i,k) - 273.16) + tem1 = exp(tem) + c0t(i,k) = c0(i) * tem1 + endif + enddo + enddo +! +!> - Initialize convective cloud water and cloud cover to zero. + do k = 1, km + do i = 1, im + cnvw(i,k) = 0. + cnvc(i,k) = 0. + enddo + enddo +! hchuang code change +!> - Initialize updraft mass fluxes to zero. + do k = 1, km + do i = 1, im + ud_mf(i,k) = 0. + dt_mf(i,k) = 0. + enddo + enddo +! + dt2 = delt +! +! model tunable parameters are all here +! clam = .3 +! aafac = .1 + aafac = .05 +! evef = 0.07 + evfact = 0.3 + evfactl = 0.3 +! +! pgcon = 0.7 ! Gregory et al. (1997, QJRMS) +! pgcon = 0.55 ! Zhang & Wu (2003,JAS) + w1l = -8.e-3 + w2l = -4.e-2 + w3l = -5.e-3 + w4l = -5.e-4 + w1s = -2.e-4 + w2s = -2.e-3 + w3s = -1.e-3 + w4s = -2.e-5 +! +! define top layer for search of the downdraft originating layer +! and the maximum thetae for updraft +! +!> - Determine maximum indices for the parcel starting point (kbm) and cloud top (kmax). + do i=1,im + kbm(i) = km + kmax(i) = km + tx1(i) = 1.0 / ps(i) + enddo +! + do k = 1, km + do i=1,im + if (prsl(i,k)*tx1(i) > 0.70) kbm(i) = k + 1 + if (prsl(i,k)*tx1(i) > 0.60) kmax(i) = k + 1 + enddo + enddo + do i=1,im + kbm(i) = min(kbm(i),kmax(i)) + enddo +! +! hydrostatic height assume zero terr and compute +! updraft entrainment rate as an inverse function of height +! +!> - Calculate hydrostatic height at layer centers assuming a flat surface (no terrain) from the geopotential. + do k = 1, km + do i=1,im + zo(i,k) = phil(i,k) / g + enddo + enddo +!> - Calculate interface height + do k = 1, km1 + do i=1,im + zi(i,k) = 0.5*(zo(i,k)+zo(i,k+1)) + enddo + enddo +! +! pbl height +! +!> - Find the index for the PBL top using the PBL height; enforce that it is lower than the maximum parcel starting level. + do i=1,im + flg(i) = cnvflg(i) + kpbl(i)= 1 + enddo + do k = 2, km1 + do i=1,im + if (flg(i) .and. zo(i,k) <= hpbl(i)) then + kpbl(i) = k + else + flg(i) = .false. + endif + enddo + enddo + do i=1,im + kpbl(i)= min(kpbl(i),kbm(i)) + enddo +! +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +! convert surface pressure to mb from cb +! +!> - Convert prsl from centibar to millibar, set normalized mass flux to 1, cloud properties to 0, and save model state variables (after advection/turbulence). + do k = 1, km + do i = 1, im + if (cnvflg(i) .and. k <= kmax(i)) then + pfld(i,k) = prsl(i,k) * 10.0 + eta(i,k) = 1. + hcko(i,k) = 0. + qcko(i,k) = 0. + qrcko(i,k)= 0. + ucko(i,k) = 0. + vcko(i,k) = 0. + dbyo(i,k) = 0. + pwo(i,k) = 0. + dellal(i,k) = 0. + to(i,k) = t1(i,k) + qo(i,k) = q1(i,k) + uo(i,k) = u1(i,k) + vo(i,k) = v1(i,k) +! uo(i,k) = u1(i,k) * rcs(i) +! vo(i,k) = v1(i,k) * rcs(i) + wu2(i,k) = 0. + buo(i,k) = 0. + drag(i,k) = 0. + cnvwt(i,k) = 0. + endif + enddo + enddo +! +! initialize tracer variables +! + do n = 3, ntr+2 + kk = n-2 + do k = 1, km + do i = 1, im + if (cnvflg(i) .and. k <= kmax(i)) then + ctr(i,k,kk) = qtr(i,k,n) + ctro(i,k,kk) = qtr(i,k,n) + ecko(i,k,kk) = 0. + endif + enddo + enddo + enddo +!> - Calculate saturation specific humidity and enforce minimum moisture values. + do k = 1, km + do i=1,im + if (cnvflg(i) .and. k <= kmax(i)) then + qeso(i,k) = 0.01 * fpvs(to(i,k)) ! fpvs is in pa + qeso(i,k) = eps * qeso(i,k) / (pfld(i,k) + epsm1*qeso(i,k)) + val1 = 1.e-8 + qeso(i,k) = max(qeso(i,k), val1) + val2 = 1.e-10 + qo(i,k) = max(qo(i,k), val2 ) +! qo(i,k) = min(qo(i,k),qeso(i,k)) +! tvo(i,k) = to(i,k) + delta * to(i,k) * qo(i,k) + endif + enddo + enddo +! +! compute moist static energy +! +!> - Calculate moist static energy (heo) and saturation moist static energy (heso). + do k = 1, km + do i=1,im + if (cnvflg(i) .and. k <= kmax(i)) then +! tem = g * zo(i,k) + cp * to(i,k) + tem = phil(i,k) + cp * to(i,k) + heo(i,k) = tem + hvap * qo(i,k) + heso(i,k) = tem + hvap * qeso(i,k) +! heo(i,k) = min(heo(i,k),heso(i,k)) + endif + enddo + enddo +! +! determine level with largest moist static energy within pbl +! this is the level where updraft starts +! +!> ## Perform calculations related to the updraft of the entraining/detraining cloud model ("static control"). +!> - Search in the PBL for the level of maximum moist static energy to start the ascending parcel. + do i=1,im + if (cnvflg(i)) then + hmax(i) = heo(i,1) + kb(i) = 1 + endif + enddo + do k = 2, km + do i=1,im + if (cnvflg(i) .and. k <= kpbl(i)) then + if(heo(i,k) > hmax(i)) then + kb(i) = k + hmax(i) = heo(i,k) + endif + endif + enddo + enddo +! +!> - Calculate the temperature, water vapor mixing ratio, and pressure at interface levels. + do k = 1, km1 + do i=1,im + if (cnvflg(i) .and. k <= kmax(i)-1) then + dz = .5 * (zo(i,k+1) - zo(i,k)) + dp = .5 * (pfld(i,k+1) - pfld(i,k)) + es = 0.01 * fpvs(to(i,k+1)) ! fpvs is in pa + pprime = pfld(i,k+1) + epsm1 * es + qs = eps * es / pprime + dqsdp = - qs / pprime + desdt = es * (fact1 / to(i,k+1) + fact2 / (to(i,k+1)**2)) + dqsdt = qs * pfld(i,k+1) * desdt / (es * pprime) + gamma = el2orc * qeso(i,k+1) / (to(i,k+1)**2) + dt = (g * dz + hvap * dqsdp * dp) / (cp * (1. + gamma)) + dq = dqsdt * dt + dqsdp * dp + to(i,k) = to(i,k+1) + dt + qo(i,k) = qo(i,k+1) + dq + po(i,k) = .5 * (pfld(i,k) + pfld(i,k+1)) + endif + enddo + enddo +! +!> - Recalculate saturation specific humidity, moist static energy, saturation moist static energy, and horizontal momentum on interface levels. Enforce minimum specific humidity. + do k = 1, km1 + do i=1,im + if (cnvflg(i) .and. k <= kmax(i)-1) then + qeso(i,k) = 0.01 * fpvs(to(i,k)) ! fpvs is in pa + qeso(i,k) = eps * qeso(i,k) / (po(i,k) + epsm1*qeso(i,k)) + val1 = 1.e-8 + qeso(i,k) = max(qeso(i,k), val1) + val2 = 1.e-10 + qo(i,k) = max(qo(i,k), val2 ) +! qo(i,k) = min(qo(i,k),qeso(i,k)) + heo(i,k) = .5 * g * (zo(i,k) + zo(i,k+1)) + & + & cp * to(i,k) + hvap * qo(i,k) + heso(i,k) = .5 * g * (zo(i,k) + zo(i,k+1)) + & + & cp * to(i,k) + hvap * qeso(i,k) + uo(i,k) = .5 * (uo(i,k) + uo(i,k+1)) + vo(i,k) = .5 * (vo(i,k) + vo(i,k+1)) + endif + enddo + enddo + do n = 1, ntr + do k = 1, km1 + do i=1,im + if (cnvflg(i) .and. k <= kmax(i)-1) then + ctro(i,k,n) = .5 * (ctro(i,k,n) + ctro(i,k+1,n)) + endif + enddo + enddo + enddo +! +! look for the level of free convection as cloud base +! +!> - Search below the index "kbm" for the level of free convection (LFC) where the condition \f$h_b > h^*\f$ is first met, where \f$h_b, h^*\f$ are the state moist static energy at the parcel's starting level and saturation moist static energy, respectively. Set "kbcon" to the index of the LFC. + do i=1,im + flg(i) = cnvflg(i) + if(flg(i)) kbcon(i) = kmax(i) + enddo + do k = 2, km1 + do i=1,im + if (flg(i) .and. k < kbm(i)) then + if(k > kb(i) .and. heo(i,kb(i)) > heso(i,k)) then + kbcon(i) = k + flg(i) = .false. + endif + endif + enddo + enddo +! + do i=1,im + if(cnvflg(i)) then + if(kbcon(i) == kmax(i)) cnvflg(i) = .false. + endif + enddo +!! +!> - If no LFC, return to the calling routine without modifying state variables. + totflg = .true. + do i=1,im + totflg = totflg .and. (.not. cnvflg(i)) + enddo + if(totflg) return +!! +!> - Determine the vertical pressure velocity at the LFC. After Han and Pan (2011) \cite han_and_pan_2011 , determine the maximum pressure thickness between a parcel's starting level and the LFC. If a parcel doesn't reach the LFC within the critical thickness, then the convective inhibition is deemed too great for convection to be triggered, and the subroutine returns to the calling routine without modifying the state variables. + do i=1,im + if(cnvflg(i)) then +! pdot(i) = 10.* dot(i,kbcon(i)) + pdot(i) = 0.01 * dot(i,kbcon(i)) ! Now dot is in Pa/s + endif + enddo +! +! turn off convection if pressure depth between parcel source level +! and cloud base is larger than a critical value, cinpcr +! + do i=1,im + if(cnvflg(i)) then + if(islimsk(i) == 1) then + w1 = w1l + w2 = w2l + w3 = w3l + w4 = w4l + else + w1 = w1s + w2 = w2s + w3 = w3s + w4 = w4s + endif + if(pdot(i) <= w4) then + tem = (pdot(i) - w4) / (w3 - w4) + elseif(pdot(i) >= -w4) then + tem = - (pdot(i) + w4) / (w4 - w3) + else + tem = 0. + endif + val1 = -1. + tem = max(tem,val1) + val2 = 1. + tem = min(tem,val2) + ptem = 1. - tem + ptem1= .5*(cinpcrmx-cinpcrmn) + cinpcr = cinpcrmx - ptem * ptem1 + tem1 = pfld(i,kb(i)) - pfld(i,kbcon(i)) + if(tem1 > cinpcr) then + cnvflg(i) = .false. + endif + endif + enddo +!! + totflg = .true. + do i=1,im + totflg = totflg .and. (.not. cnvflg(i)) + enddo + if(totflg) return +! +! turbulent entrainment rate assumed to be proportional +! to subcloud mean TKE +! + if(ntk > 0) then +! + do i= 1, im + if(cnvflg(i)) then + sumx(i) = 0. + tkemean(i) = 0. + endif + enddo + do k = 1, km1 + do i = 1, im + if(cnvflg(i)) then + if(k >= kb(i) .and. k < kbcon(i)) then + dz = zo(i,k+1) - zo(i,k) + tem = 0.5 * (qtr(i,k,ntk)+qtr(i,k+1,ntk)) + tkemean(i) = tkemean(i) + tem * dz + sumx(i) = sumx(i) + dz + endif + endif + enddo + enddo +! + do i= 1, im + if(cnvflg(i)) then + tkemean(i) = tkemean(i) / sumx(i) + if(tkemean(i) > tkemx) then + clamt(i) = clam + clamd + else if(tkemean(i) < tkemn) then + clamt(i) = clam - clamd + else + tem = tkemx - tkemean(i) + tem1 = 1. - 2. * tem / dtke + clamt(i) = clam + clamd * tem1 + endif + endif + enddo +! + else +! + do i= 1, im + if(cnvflg(i)) then + clamt(i) = clam + endif + enddo +! + endif +!! +! +! assume updraft entrainment rate +! is an inverse function of height +! + do k = 1, km1 + do i=1,im + if(cnvflg(i)) then + xlamue(i,k) = clamt(i) / zi(i,k) + endif + enddo + enddo + do i=1,im + if(cnvflg(i)) then + xlamue(i,km) = xlamue(i,km1) + endif + enddo +! +! specify the detrainment rate for the updrafts +! +!! (The updraft detrainment rate is set constant and equal to the entrainment rate at cloud base.) +!! +!> - The updraft detrainment rate is vertically constant and proportional to clamt + do i = 1, im + if(cnvflg(i)) then +! xlamud(i) = xlamue(i,kbcon(i)) +! xlamud(i) = crtlamd + xlamud(i) = 0.001 * clamt(i) + endif + enddo +! +! determine updraft mass flux for the subcloud layers +! +!> - Calculate the normalized mass flux for subcloud and in-cloud layers according to Pan and Wu (1995) \cite pan_and_wu_1995 equation 1: +!! \f[ +!! \frac{1}{\eta}\frac{\partial \eta}{\partial z} = \lambda_e - \lambda_d +!! \f] +!! where \f$\eta\f$ is the normalized mass flux, \f$\lambda_e\f$ is the entrainment rate and \f$\lambda_d\f$ is the detrainment rate. The normalized mass flux increases upward below the cloud base and decreases upward above. + do k = km1, 1, -1 + do i = 1, im + if (cnvflg(i)) then + if(k < kbcon(i) .and. k >= kb(i)) then + dz = zi(i,k+1) - zi(i,k) + ptem = 0.5*(xlamue(i,k)+xlamue(i,k+1))-xlamud(i) + eta(i,k) = eta(i,k+1) / (1. + ptem * dz) + endif + endif + enddo + enddo +! +! compute mass flux above cloud base +! + do i = 1, im + flg(i) = cnvflg(i) + enddo + do k = 2, km1 + do i = 1, im + if(flg(i))then + if(k > kbcon(i) .and. k < kmax(i)) then + dz = zi(i,k) - zi(i,k-1) + ptem = 0.5*(xlamue(i,k)+xlamue(i,k-1))-xlamud(i) + eta(i,k) = eta(i,k-1) * (1 + ptem * dz) + if(eta(i,k) <= 0.) then + kmax(i) = k + ktconn(i) = k + kbm(i) = min(kbm(i),kmax(i)) + flg(i) = .false. + endif + endif + endif + enddo + enddo +! +! compute updraft cloud property +! +!> - Set cloud properties equal to the state variables at updraft starting level (kb). + do i = 1, im + if(cnvflg(i)) then + indx = kb(i) + hcko(i,indx) = heo(i,indx) + ucko(i,indx) = uo(i,indx) + vcko(i,indx) = vo(i,indx) + endif + enddo +! for tracers + do n = 1, ntr + do i = 1, im + if(cnvflg(i)) then + indx = kb(i) + ecko(i,indx,n) = ctro(i,indx,n) + endif + enddo + enddo +! +! cm is an enhancement factor in entrainment rates for momentum +! +!> - Calculate the cloud properties as a parcel ascends, modified by entrainment and detrainment. Discretization follows Appendix B of Grell (1993) \cite grell_1993 . Following Han and Pan (2006) \cite han_and_pan_2006, the convective momentum transport is reduced by the convection-induced pressure gradient force by the constant "pgcon", currently set to 0.55 after Zhang and Wu (2003) \cite zhang_and_wu_2003 . + do k = 2, km1 + do i = 1, im + if (cnvflg(i)) then + if(k > kb(i) .and. k < kmax(i)) then + dz = zi(i,k) - zi(i,k-1) + tem = 0.5 * (xlamue(i,k)+xlamue(i,k-1)) * dz + tem1 = 0.5 * xlamud(i) * dz + factor = 1. + tem - tem1 + hcko(i,k) = ((1.-tem1)*hcko(i,k-1)+tem*0.5* & + & (heo(i,k)+heo(i,k-1)))/factor + dbyo(i,k) = hcko(i,k) - heso(i,k) +! + tem = 0.5 * cm * tem + factor = 1. + tem + ptem = tem + pgcon + ptem1= tem - pgcon + ucko(i,k) = ((1.-tem)*ucko(i,k-1)+ptem*uo(i,k) & + & +ptem1*uo(i,k-1))/factor + vcko(i,k) = ((1.-tem)*vcko(i,k-1)+ptem*vo(i,k) & + & +ptem1*vo(i,k-1))/factor + endif + endif + enddo + enddo + do n = 1, ntr + do k = 2, km1 + do i = 1, im + if (cnvflg(i)) then + if(k > kb(i) .and. k < kmax(i)) then + dz = zi(i,k) - zi(i,k-1) + tem = 0.25 * (xlamue(i,k)+xlamue(i,k-1)) * dz + factor = 1. + tem + ecko(i,k,n) = ((1.-tem)*ecko(i,k-1,n)+tem* & + & (ctro(i,k,n)+ctro(i,k-1,n)))/factor + endif + endif + enddo + enddo + enddo +! +! taking account into convection inhibition due to existence of +! dry layers below cloud base +! +!> - With entrainment, recalculate the LFC as the first level where buoyancy is positive. The difference in pressure levels between LFCs calculated with/without entrainment must be less than a threshold (currently 25 hPa). Otherwise, convection is inhibited and the scheme returns to the calling routine without modifying the state variables. This is the subcloud dryness trigger modification discussed in Han and Pan (2011) \cite han_and_pan_2011. + do i=1,im + flg(i) = cnvflg(i) + kbcon1(i) = kmax(i) + enddo + do k = 2, km1 + do i=1,im + if (flg(i) .and. k < kbm(i)) then + if(k >= kbcon(i) .and. dbyo(i,k) > 0.) then + kbcon1(i) = k + flg(i) = .false. + endif + endif + enddo + enddo + do i=1,im + if(cnvflg(i)) then + if(kbcon1(i) == kmax(i)) cnvflg(i) = .false. + endif + enddo + do i=1,im + if(cnvflg(i)) then + tem = pfld(i,kbcon(i)) - pfld(i,kbcon1(i)) + if(tem > dthk) then + cnvflg(i) = .false. + endif + endif + enddo +!! + totflg = .true. + do i = 1, im + totflg = totflg .and. (.not. cnvflg(i)) + enddo + if(totflg) return +!! +! +! calculate convective inhibition +! +!> - Calculate additional trigger condition of the convective inhibition (CIN) according to Han et al.'s (2017) \cite han_et_al_2017 equation 13. + do k = 2, km1 + do i = 1, im + if (cnvflg(i)) then + if(k > kb(i) .and. k < kbcon1(i)) then + dz1 = zo(i,k+1) - zo(i,k) + gamma = el2orc * qeso(i,k) / (to(i,k)**2) + rfact = 1. + delta * cp * gamma & + & * to(i,k) / hvap + cina(i) = cina(i) + & +! & dz1 * eta(i,k) * (g / (cp * to(i,k))) + & dz1 * (g / (cp * to(i,k))) & + & * dbyo(i,k) / (1. + gamma) & + & * rfact + val = 0. + cina(i) = cina(i) + & +! & dz1 * eta(i,k) * g * delta * + & dz1 * g * delta * & + & max(val,(qeso(i,k) - qo(i,k))) + endif + endif + enddo + enddo +!> - Turn off convection if the CIN is less than a critical value (cinacr) which is inversely proportional to the large-scale vertical velocity. + do i = 1, im + if(cnvflg(i)) then +! + if(islimsk(i) == 1) then + w1 = w1l + w2 = w2l + w3 = w3l + w4 = w4l + else + w1 = w1s + w2 = w2s + w3 = w3s + w4 = w4s + endif + if(pdot(i) <= w4) then + tem = (pdot(i) - w4) / (w3 - w4) + elseif(pdot(i) >= -w4) then + tem = - (pdot(i) + w4) / (w4 - w3) + else + tem = 0. + endif + + val1 = -1. + tem = max(tem,val1) + val2 = 1. + tem = min(tem,val2) + tem = 1. - tem + tem1= .5*(cinacrmx-cinacrmn) + cinacr = cinacrmx - tem * tem1 +! +! cinacr = cinacrmx + if(cina(i) < cinacr) cnvflg(i) = .false. + endif + enddo +!! + totflg = .true. + do i=1,im + totflg = totflg .and. (.not. cnvflg(i)) + enddo + if(totflg) return +!! +! +! determine first guess cloud top as the level of zero buoyancy +! limited to the level of P/Ps=0.7 +! +!> - Calculate the cloud top as the first level where parcel buoyancy becomes negative; the maximum possible value is at \f$p=0.7p_{sfc}\f$. + do i = 1, im + flg(i) = cnvflg(i) + if(flg(i)) ktcon(i) = kbm(i) + enddo + do k = 2, km1 + do i=1,im + if (flg(i) .and. k < kbm(i)) then + if(k > kbcon1(i) .and. dbyo(i,k) < 0.) then + ktcon(i) = k + flg(i) = .false. + endif + endif + enddo + enddo +! +! specify upper limit of mass flux at cloud base +! +!> - Calculate the maximum value of the cloud base mass flux using the CFL-criterion-based formula of Han and Pan (2011) \cite han_and_pan_2011, equation 7. + do i = 1, im + if(cnvflg(i)) then +! xmbmax(i) = .1 +! + k = kbcon(i) + dp = 1000. * del(i,k) + xmbmax(i) = dp / (2. * g * dt2) +! +! xmbmax(i) = dp / (g * dt2) +! +! tem = dp / (g * dt2) +! xmbmax(i) = min(tem, xmbmax(i)) + endif + enddo +! +! compute cloud moisture property and precipitation +! +!> - Set cloud moisture property equal to the enviromental moisture at updraft starting level (kb). + do i = 1, im + if (cnvflg(i)) then + aa1(i) = 0. + qcko(i,kb(i)) = qo(i,kb(i)) + qrcko(i,kb(i)) = qo(i,kb(i)) + endif + enddo +!> - Calculate the moisture content of the entraining/detraining parcel (qcko) and the value it would have if just saturated (qrch), according to equation A.14 in Grell (1993) \cite grell_1993 . Their difference is the amount of convective cloud water (qlk = rain + condensate). Determine the portion of convective cloud water that remains suspended and the portion that is converted into convective precipitation (pwo). Calculate and save the negative cloud work function (aa1) due to water loading. Above the level of minimum moist static energy, some of the cloud water is detrained into the grid-scale cloud water from every cloud layer with a rate of 0.0005 \f$m^{-1}\f$ (dellal). + do k = 2, km1 + do i = 1, im + if (cnvflg(i)) then + if(k > kb(i) .and. k < ktcon(i)) then + dz = zi(i,k) - zi(i,k-1) + gamma = el2orc * qeso(i,k) / (to(i,k)**2) + qrch = qeso(i,k) & + & + gamma * dbyo(i,k) / (hvap * (1. + gamma)) +!j + tem = 0.5 * (xlamue(i,k)+xlamue(i,k-1)) * dz + tem1 = 0.5 * xlamud(i) * dz + factor = 1. + tem - tem1 + qcko(i,k) = ((1.-tem1)*qcko(i,k-1)+tem*0.5* & + & (qo(i,k)+qo(i,k-1)))/factor + qrcko(i,k) = qcko(i,k) +!j + dq = eta(i,k) * (qcko(i,k) - qrch) +! +! rhbar(i) = rhbar(i) + qo(i,k) / qeso(i,k) +! +! below lfc check if there is excess moisture to release latent heat +! + if(k >= kbcon(i) .and. dq > 0.) then + etah = .5 * (eta(i,k) + eta(i,k-1)) + dp = 1000. * del(i,k) + if(ncloud > 0) then + ptem = c0t(i,k) + c1 + qlk = dq / (eta(i,k) + etah * ptem * dz) + dellal(i,k) = etah * c1 * dz * qlk * g / dp + else + qlk = dq / (eta(i,k) + etah * c0t(i,k) * dz) + endif + buo(i,k) = buo(i,k) - g * qlk + qcko(i,k)= qlk + qrch + pwo(i,k) = etah * c0t(i,k) * dz * qlk + cnvwt(i,k) = etah * qlk * g / dp + endif +! +! compute buoyancy and drag for updraft velocity +! + if(k >= kbcon(i)) then + rfact = 1. + delta * cp * gamma & + & * to(i,k) / hvap + buo(i,k) = buo(i,k) + (g / (cp * to(i,k))) & + & * dbyo(i,k) / (1. + gamma) & + & * rfact + val = 0. + buo(i,k) = buo(i,k) + g * delta * & + & max(val,(qeso(i,k) - qo(i,k))) + drag(i,k) = max(xlamue(i,k),xlamud(i)) + endif +! + endif + endif + enddo + enddo +! +! calculate cloud work function +! +! do k = 2, km1 +! do i = 1, im +! if (cnvflg(i)) then +! if(k >= kbcon(i) .and. k < ktcon(i)) then +! dz1 = zo(i,k+1) - zo(i,k) +! gamma = el2orc * qeso(i,k) / (to(i,k)**2) +! rfact = 1. + delta * cp * gamma +! & * to(i,k) / hvap +! aa1(i) = aa1(i) + +!! & dz1 * eta(i,k) * (g / (cp * to(i,k))) +! & dz1 * (g / (cp * to(i,k))) +! & * dbyo(i,k) / (1. + gamma) +! & * rfact +! val = 0. +! aa1(i) = aa1(i) + +!! & dz1 * eta(i,k) * g * delta * +! & dz1 * g * delta * +! & max(val,(qeso(i,k) - qo(i,k))) +! endif +! endif +! enddo +! enddo +! do i = 1, im +! if(cnvflg(i) .and. aa1(i) <= 0.) cnvflg(i) = .false. +! enddo +! +! calculate cloud work function +! +!> - Calculate the cloud work function according to Pan and Wu (1995) \cite pan_and_wu_1995 equation 4: +!! \f[ +!! A_u=\int_{z_0}^{z_t}\frac{g}{c_pT(z)}\frac{\eta}{1 + \gamma}[h(z)-h^*(z)]dz +!! \f] +!! (discretized according to Grell (1993) \cite grell_1993 equation B.10 using B.2 and B.3 of Arakawa and Schubert (1974) \cite arakawa_and_schubert_1974 and assuming \f$\eta=1\f$) where \f$A_u\f$ is the updraft cloud work function, \f$z_0\f$ and \f$z_t\f$ are cloud base and cloud top, respectively, \f$\gamma = \frac{L}{c_p}\left(\frac{\partial \overline{q_s}}{\partial T}\right)_p\f$ and other quantities are previously defined. + do i = 1, im + if (cnvflg(i)) then + aa1(i) = 0. + endif + enddo + do k = 2, km1 + do i = 1, im + if (cnvflg(i)) then + if(k >= kbcon(i) .and. k < ktcon(i)) then + dz1 = zo(i,k+1) - zo(i,k) + aa1(i) = aa1(i) + buo(i,k) * dz1 + endif + endif + enddo + enddo + do i = 1, im + if(cnvflg(i) .and. aa1(i) <= 0.) cnvflg(i) = .false. + enddo +!! +!> - If the updraft cloud work function is negative, convection does not occur, and the scheme returns to the calling routine. + totflg = .true. + do i=1,im + totflg = totflg .and. (.not. cnvflg(i)) + enddo + if(totflg) return +!! +! +! estimate the onvective overshooting as the level +! where the [aafac * cloud work function] becomes zero, +! which is the final cloud top +! limited to the level of P/Ps=0.7 +! +!> - Continue calculating the cloud work function past the point of neutral buoyancy to represent overshooting according to Han and Pan (2011) \cite han_and_pan_2011 . Convective overshooting stops when \f$ cA_u < 0\f$ where \f$c\f$ is currently 10%, or when 10% of the updraft cloud work function has been consumed by the stable buoyancy force. Overshooting is also limited to the level where \f$p=0.7p_{sfc}\f$. + do i = 1, im + if (cnvflg(i)) then + aa1(i) = aafac * aa1(i) + endif + enddo +! + do i = 1, im + flg(i) = cnvflg(i) + ktcon1(i) = kbm(i) + enddo + do k = 2, km1 + do i = 1, im + if (flg(i)) then + if(k >= ktcon(i) .and. k < kbm(i)) then + dz1 = zo(i,k+1) - zo(i,k) + gamma = el2orc * qeso(i,k) / (to(i,k)**2) + rfact = 1. + delta * cp * gamma & + & * to(i,k) / hvap + aa1(i) = aa1(i) + & +! & dz1 * eta(i,k) * (g / (cp * to(i,k))) + & dz1 * (g / (cp * to(i,k))) & + & * dbyo(i,k) / (1. + gamma) & + & * rfact +! val = 0. +! aa1(i) = aa1(i) + +!! & dz1 * eta(i,k) * g * delta * +! & dz1 * g * delta * +! & max(val,(qeso(i,k) - qo(i,k))) + if(aa1(i) < 0.) then + ktcon1(i) = k + flg(i) = .false. + endif + endif + endif + enddo + enddo +! +! compute cloud moisture property, detraining cloud water +! and precipitation in overshooting layers +! +!> - For the overshooting convection, calculate the moisture content of the entraining/detraining parcel as before. Partition convective cloud water and precipitation and detrain convective cloud water in the overshooting layers. + do k = 2, km1 + do i = 1, im + if (cnvflg(i)) then + if(k >= ktcon(i) .and. k < ktcon1(i)) then + dz = zi(i,k) - zi(i,k-1) + gamma = el2orc * qeso(i,k) / (to(i,k)**2) + qrch = qeso(i,k) & + & + gamma * dbyo(i,k) / (hvap * (1. + gamma)) +!j + tem = 0.5 * (xlamue(i,k)+xlamue(i,k-1)) * dz + tem1 = 0.5 * xlamud(i) * dz + factor = 1. + tem - tem1 + qcko(i,k) = ((1.-tem1)*qcko(i,k-1)+tem*0.5* & + & (qo(i,k)+qo(i,k-1)))/factor + qrcko(i,k) = qcko(i,k) +!j + dq = eta(i,k) * (qcko(i,k) - qrch) +! +! check if there is excess moisture to release latent heat +! + if(dq > 0.) then + etah = .5 * (eta(i,k) + eta(i,k-1)) + dp = 1000. * del(i,k) + if(ncloud > 0) then + ptem = c0t(i,k) + c1 + qlk = dq / (eta(i,k) + etah * ptem * dz) + dellal(i,k) = etah * c1 * dz * qlk * g / dp + else + qlk = dq / (eta(i,k) + etah * c0t(i,k) * dz) + endif + qcko(i,k) = qlk + qrch + pwo(i,k) = etah * c0t(i,k) * dz * qlk + cnvwt(i,k) = etah * qlk * g / dp + endif + endif + endif + enddo + enddo +! +! compute updraft velocity square(wu2) +!> - Calculate updraft velocity square(wu2) according to Han et al.'s (2017) \cite han_et_al_2017 equation 7. +! +! bb1 = 2. * (1.+bet1*cd1) +! bb2 = 2. / (f1*(1.+gam1)) +! +! bb1 = 3.9 +! bb2 = 0.67 +! +! bb1 = 2.0 +! bb2 = 4.0 +! + bb1 = 4.0 + bb2 = 0.8 +! +! do i = 1, im +! if (cnvflg(i)) then +! k = kbcon1(i) +! tem = po(i,k) / (rd * to(i,k)) +! wucb = -0.01 * dot(i,k) / (tem * g) +! if(wucb > 0.) then +! wu2(i,k) = wucb * wucb +! else +! wu2(i,k) = 0. +! endif +! endif +! enddo + do k = 2, km1 + do i = 1, im + if (cnvflg(i)) then + if(k > kbcon1(i) .and. k < ktcon(i)) then + dz = zi(i,k) - zi(i,k-1) + tem = 0.25 * bb1 * (drag(i,k)+drag(i,k-1)) * dz + tem1 = 0.5 * bb2 * (buo(i,k)+buo(i,k-1)) * dz + ptem = (1. - tem) * wu2(i,k-1) + ptem1 = 1. + tem + wu2(i,k) = (ptem + tem1) / ptem1 + wu2(i,k) = max(wu2(i,k), 0.) + endif + endif + enddo + enddo +! +! compute updraft velocity averaged over the whole cumulus +! +!> - Calculate the mean updraft velocity within the cloud (wc). + do i = 1, im + wc(i) = 0. + sumx(i) = 0. + enddo + do k = 2, km1 + do i = 1, im + if (cnvflg(i)) then + if(k > kbcon1(i) .and. k < ktcon(i)) then + dz = zi(i,k) - zi(i,k-1) + tem = 0.5 * (sqrt(wu2(i,k)) + sqrt(wu2(i,k-1))) + wc(i) = wc(i) + tem * dz + sumx(i) = sumx(i) + dz + endif + endif + enddo + enddo + do i = 1, im + if(cnvflg(i)) then + if(sumx(i) == 0.) then + cnvflg(i)=.false. + else + wc(i) = wc(i) / sumx(i) + endif + val = 1.e-4 + if (wc(i) < val) cnvflg(i)=.false. + endif + enddo +! +! exchange ktcon with ktcon1 +! + do i = 1, im + if(cnvflg(i)) then + kk = ktcon(i) + ktcon(i) = ktcon1(i) + ktcon1(i) = kk + endif + enddo +! +! this section is ready for cloud water +! + if(ncloud > 0) then +! +! compute liquid and vapor separation at cloud top +! +!> - => Separate the total updraft cloud water at cloud top into vapor and condensate. + do i = 1, im + if(cnvflg(i)) then + k = ktcon(i) - 1 + gamma = el2orc * qeso(i,k) / (to(i,k)**2) + qrch = qeso(i,k) & + & + gamma * dbyo(i,k) / (hvap * (1. + gamma)) + dq = qcko(i,k) - qrch +! +! check if there is excess moisture to release latent heat +! + if(dq > 0.) then + qlko_ktcon(i) = dq + qcko(i,k) = qrch + endif + endif + enddo + endif +! +!--- compute precipitation efficiency in terms of windshear +! +!! - Calculate the wind shear and precipitation efficiency according to equation 58 in Fritsch and Chappell (1980) \cite fritsch_and_chappell_1980 : +!! \f[ +!! E = 1.591 - 0.639\frac{\Delta V}{\Delta z} + 0.0953\left(\frac{\Delta V}{\Delta z}\right)^2 - 0.00496\left(\frac{\Delta V}{\Delta z}\right)^3 +!! \f] +!! where \f$\Delta V\f$ is the integrated horizontal shear over the cloud depth, \f$\Delta z\f$, (the ratio is converted to units of \f$10^{-3} s^{-1}\f$). The variable "edt" is \f$1-E\f$ and is constrained to the range \f$[0,0.9]\f$. + do i = 1, im + if(cnvflg(i)) then + vshear(i) = 0. + endif + enddo + do k = 2, km + do i = 1, im + if (cnvflg(i)) then + if(k > kb(i) .and. k <= ktcon(i)) then + shear= sqrt((uo(i,k)-uo(i,k-1)) ** 2 & + & + (vo(i,k)-vo(i,k-1)) ** 2) + vshear(i) = vshear(i) + shear + endif + endif + enddo + enddo + do i = 1, im + if(cnvflg(i)) then + vshear(i) = 1.e3 * vshear(i) / (zi(i,ktcon(i))-zi(i,kb(i))) + e1=1.591-.639*vshear(i) & + & +.0953*(vshear(i)**2)-.00496*(vshear(i)**3) + edt(i)=1.-e1 + val = .9 + edt(i) = min(edt(i),val) + val = .0 + edt(i) = max(edt(i),val) + endif + enddo +! +!--- what would the change be, that a cloud with unit mass +!--- will do to the environment? +! +!> ## Calculate the tendencies of the state variables (per unit cloud base mass flux) and the cloud base mass flux. +!> - Calculate the change in moist static energy, moisture mixing ratio, and horizontal winds per unit cloud base mass flux for all layers below cloud top from equations B.14 and B.15 from Grell (1993) \cite grell_1993, and for the cloud top from B.16 and B.17. + do k = 1, km + do i = 1, im + if(cnvflg(i) .and. k <= kmax(i)) then + dellah(i,k) = 0. + dellaq(i,k) = 0. + dellau(i,k) = 0. + dellav(i,k) = 0. + endif + enddo + enddo + do n = 1, ntr + do k = 1, km + do i = 1, im + if(cnvflg(i) .and. k <= kmax(i)) then + dellae(i,k,n) = 0. + endif + enddo + enddo + enddo +! +!--- changed due to subsidence and entrainment +! + do k = 2, km1 + do i = 1, im + if (cnvflg(i)) then + if(k > kb(i) .and. k < ktcon(i)) then + dp = 1000. * del(i,k) + dz = zi(i,k) - zi(i,k-1) +! + dv1h = heo(i,k) + dv2h = .5 * (heo(i,k) + heo(i,k-1)) + dv3h = heo(i,k-1) + dv1q = qo(i,k) + dv2q = .5 * (qo(i,k) + qo(i,k-1)) + dv3q = qo(i,k-1) +! + tem = 0.5 * (xlamue(i,k)+xlamue(i,k-1)) + tem1 = xlamud(i) +!j + dellah(i,k) = dellah(i,k) + & + & ( eta(i,k)*dv1h - eta(i,k-1)*dv3h & + & - tem*eta(i,k-1)*dv2h*dz & + & + tem1*eta(i,k-1)*.5*(hcko(i,k)+hcko(i,k-1))*dz & + & ) *g/dp +!j + dellaq(i,k) = dellaq(i,k) + & + & ( eta(i,k)*dv1q - eta(i,k-1)*dv3q & + & - tem*eta(i,k-1)*dv2q*dz & + & + tem1*eta(i,k-1)*.5*(qrcko(i,k)+qcko(i,k-1))*dz & + & ) *g/dp +!j + tem1=eta(i,k)*(uo(i,k)-ucko(i,k)) + tem2=eta(i,k-1)*(uo(i,k-1)-ucko(i,k-1)) + dellau(i,k) = dellau(i,k) + (tem1-tem2) * g/dp +!j + tem1=eta(i,k)*(vo(i,k)-vcko(i,k)) + tem2=eta(i,k-1)*(vo(i,k-1)-vcko(i,k-1)) + dellav(i,k) = dellav(i,k) + (tem1-tem2) * g/dp +!j + endif + endif + enddo + enddo + do n = 1, ntr + do k = 2, km1 + do i = 1, im + if (cnvflg(i)) then + if(k > kb(i) .and. k < ktcon(i)) then + dp = 1000. * del(i,k) +!j + tem1=eta(i,k)*(ctro(i,k,n)-ecko(i,k,n)) + tem2=eta(i,k-1)*(ctro(i,k-1,n)-ecko(i,k-1,n)) + dellae(i,k,n) = dellae(i,k,n) + (tem1-tem2) * g/dp +!j + endif + endif + enddo + enddo + enddo +! +!------- cloud top +! + do i = 1, im + if(cnvflg(i)) then + indx = ktcon(i) + dp = 1000. * del(i,indx) + dv1h = heo(i,indx-1) + dellah(i,indx) = eta(i,indx-1) * & + & (hcko(i,indx-1) - dv1h) * g / dp + dv1q = qo(i,indx-1) + dellaq(i,indx) = eta(i,indx-1) * & + & (qcko(i,indx-1) - dv1q) * g / dp + dellau(i,indx) = eta(i,indx-1) * & + & (ucko(i,indx-1) - uo(i,indx-1)) * g / dp + dellav(i,indx) = eta(i,indx-1) * & + & (vcko(i,indx-1) - vo(i,indx-1)) * g / dp +! +! cloud water +! + dellal(i,indx) = eta(i,indx-1) * & + & qlko_ktcon(i) * g / dp + endif + enddo + do n = 1, ntr + do i = 1, im + if(cnvflg(i)) then + indx = ktcon(i) + dp = 1000. * del(i,indx) + dellae(i,indx,n) = eta(i,indx-1) * & + & (ecko(i,indx-1,n) - ctro(i,indx-1,n)) * g / dp + endif + enddo + enddo +! +! compute convective turn-over time +! +!> - Following Bechtold et al. (2008) \cite bechtold_et_al_2008, calculate the convective turnover time using the mean updraft velocity (wc) and the cloud depth. It is also proportional to the grid size (gdx). + do i= 1, im + if(cnvflg(i)) then + tem = zi(i,ktcon1(i)) - zi(i,kbcon1(i)) + dtconv(i) = tem / wc(i) + tfac = 1. + gdx(i) / 75000. + dtconv(i) = tfac * dtconv(i) + dtconv(i) = max(dtconv(i),dtmin) + dtconv(i) = max(dtconv(i),dt2) + dtconv(i) = min(dtconv(i),dtmax) + endif + enddo +! +!> - Calculate advective time scale (tauadv) using a mean cloud layer wind speed. + do i= 1, im + if(cnvflg(i)) then + sumx(i) = 0. + umean(i) = 0. + endif + enddo + do k = 2, km1 + do i = 1, im + if(cnvflg(i)) then + if(k >= kbcon1(i) .and. k < ktcon1(i)) then + dz = zi(i,k) - zi(i,k-1) + tem = sqrt(u1(i,k)*u1(i,k)+v1(i,k)*v1(i,k)) + umean(i) = umean(i) + tem * dz + sumx(i) = sumx(i) + dz + endif + endif + enddo + enddo + do i= 1, im + if(cnvflg(i)) then + umean(i) = umean(i) / sumx(i) + umean(i) = max(umean(i), 1.) + tauadv(i) = gdx(i) / umean(i) + endif + enddo +! +! compute cloud base mass flux as a function of the mean +! updraft velcoity +! +!> - From Han et al.'s (2017) \cite han_et_al_2017 equation 6, calculate cloud base mass flux as a function of the mean updraft velcoity. +!! As discussed in Han et al. (2017) \cite han_et_al_2017 , when dtconv is larger than tauadv, the convective mixing is not fully conducted before the cumulus cloud is advected out of the grid cell. In this case, therefore, the cloud base mass flux is further reduced in proportion to the ratio of tauadv to dtconv. + do i= 1, im + if(cnvflg(i)) then + k = kbcon(i) + rho = po(i,k)*100. / (rd*to(i,k)) + tfac = tauadv(i) / dtconv(i) + tfac = min(tfac, 1.) + xmb(i) = tfac*betaw*rho*wc(i) + endif + enddo +! +!> - For scale-aware parameterization, the updraft fraction (sigmagfm) is first computed as a function of the lateral entrainment rate at cloud base (see Han et al.'s (2017) \cite han_et_al_2017 equation 4 and 5), following the study by Grell and Freitas (2014) \cite grell_and_freitus_2014. + do i = 1, im + if(cnvflg(i)) then + tem = min(max(xlamue(i,kbcon(i)), 2.e-4), 6.e-4) + tem = 0.2 / tem + tem1 = 3.14 * tem * tem + sigmagfm(i) = tem1 / garea(i) + sigmagfm(i) = max(sigmagfm(i), 0.001) + sigmagfm(i) = min(sigmagfm(i), 0.999) + endif + enddo +! +!> - Then, calculate the reduction factor (scaldfunc) of the vertical convective eddy transport of mass flux as a function of updraft fraction from the studies by Arakawa and Wu (2013) \cite arakawa_and_wu_2013 (also see Han et al.'s (2017) \cite han_et_al_2017 equation 1 and 2). The final cloud base mass flux with scale-aware parameterization is obtained from the mass flux when sigmagfm << 1, multiplied by the reduction factor (Han et al.'s (2017) \cite han_et_al_2017 equation 2). + do i = 1, im + if(cnvflg(i)) then + if (gdx(i) < dxcrt) then + scaldfunc(i) = (1.-sigmagfm(i)) * (1.-sigmagfm(i)) + scaldfunc(i) = max(min(scaldfunc(i), 1.0), 0.) + else + scaldfunc(i) = 1.0 + endif + xmb(i) = xmb(i) * scaldfunc(i) + xmb(i) = min(xmb(i),xmbmax(i)) + endif + enddo + +! +! transport aerosols if present +! + if (do_aerosols) & + & call samfshalcnv_aerosols(im, ix, km, itc, ntc, ntr, delt, & +! & xlamde, xlamdd, cnvflg, jmin, kb, kmax, kbcon, ktcon, fscav, + & cnvflg, kb, kmax, kbcon, ktcon, fscav, & +! & edto, xlamd, xmb, c0t, eta, etad, zi, xlamue, xlamud, delp, + & xmb, c0t, eta, zi, xlamue, xlamud, delp, & + & qtr, qaero) + +!> ## For the "feedback control", calculate updated values of the state variables by multiplying the cloud base mass flux and the tendencies calculated per unit cloud base mass flux from the static control. +!! - Recalculate saturation specific humidity. +! +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +! + do k = 1, km + do i = 1, im + if (cnvflg(i) .and. k <= kmax(i)) then + qeso(i,k) = 0.01 * fpvs(t1(i,k)) ! fpvs is in pa + qeso(i,k) = eps * qeso(i,k) / (pfld(i,k) + epsm1*qeso(i,k)) + val = 1.e-8 + qeso(i,k) = max(qeso(i,k), val ) + endif + enddo + enddo +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +! +!> - Calculate the temperature tendency from the moist static energy and specific humidity tendencies. +!> - Update the temperature, specific humidity, and horiztonal wind state variables by multiplying the cloud base mass flux-normalized tendencies by the cloud base mass flux. +!> - Accumulate column-integrated tendencies. + do i = 1, im + delhbar(i) = 0. + delqbar(i) = 0. + deltbar(i) = 0. + delubar(i) = 0. + delvbar(i) = 0. + qcond(i) = 0. + enddo + do n = 1, ntr + do i = 1, im + delebar(i,n) = 0. + enddo + enddo + do k = 1, km + do i = 1, im + if (cnvflg(i)) then + if(k > kb(i) .and. k <= ktcon(i)) then + dellat = (dellah(i,k) - hvap * dellaq(i,k)) / cp + t1(i,k) = t1(i,k) + dellat * xmb(i) * dt2 + q1(i,k) = q1(i,k) + dellaq(i,k) * xmb(i) * dt2 +! tem = 1./rcs(i) +! u1(i,k) = u1(i,k) + dellau(i,k) * xmb(i) * dt2 * tem +! v1(i,k) = v1(i,k) + dellav(i,k) * xmb(i) * dt2 * tem + u1(i,k) = u1(i,k) + dellau(i,k) * xmb(i) * dt2 + v1(i,k) = v1(i,k) + dellav(i,k) * xmb(i) * dt2 + dp = 1000. * del(i,k) + delhbar(i) = delhbar(i) + dellah(i,k)*xmb(i)*dp/g + delqbar(i) = delqbar(i) + dellaq(i,k)*xmb(i)*dp/g + deltbar(i) = deltbar(i) + dellat*xmb(i)*dp/g + delubar(i) = delubar(i) + dellau(i,k)*xmb(i)*dp/g + delvbar(i) = delvbar(i) + dellav(i,k)*xmb(i)*dp/g + endif + endif + enddo + enddo + do n = 1, ntr + kk = n+2 + do k = 1, km + do i = 1, im + if (cnvflg(i) .and. k <= kmax(i)) then + if(k <= ktcon(i)) then + ctr(i,k,n) = ctr(i,k,n)+dellae(i,k,n)*xmb(i)*dt2 + delebar(i,n)=delebar(i,n)+dellae(i,k,n)*xmb(i)*dp/g + qtr(i,k,kk) = ctr(i,k,n) + endif + endif + enddo + enddo + enddo +! +!> - Recalculate saturation specific humidity using the updated temperature. + do k = 1, km + do i = 1, im + if (cnvflg(i)) then + if(k > kb(i) .and. k <= ktcon(i)) then + qeso(i,k) = 0.01 * fpvs(t1(i,k)) ! fpvs is in pa + qeso(i,k) = eps * qeso(i,k)/(pfld(i,k) + epsm1*qeso(i,k)) + val = 1.e-8 + qeso(i,k) = max(qeso(i,k), val ) + endif + endif + enddo + enddo +! +!> - Add up column-integrated convective precipitation by multiplying the normalized value by the cloud base mass flux. + do i = 1, im + rntot(i) = 0. + delqev(i) = 0. + delq2(i) = 0. + flg(i) = cnvflg(i) + enddo + do k = km, 1, -1 + do i = 1, im + if (cnvflg(i)) then + if(k < ktcon(i) .and. k > kb(i)) then + rntot(i) = rntot(i) + pwo(i,k) * xmb(i) * .001 * dt2 + endif + endif + enddo + enddo +! +! evaporating rain +! +!> - Determine the evaporation of the convective precipitation and update the integrated convective precipitation. +!> - Update state temperature and moisture to account for evaporation of convective precipitation. +!> - Update column-integrated tendencies to account for evaporation of convective precipitation. + do k = km, 1, -1 + do i = 1, im + if (k <= kmax(i)) then + deltv(i) = 0. + delq(i) = 0. + qevap(i) = 0. + if(cnvflg(i)) then + if(k < ktcon(i) .and. k > kb(i)) then + rn(i) = rn(i) + pwo(i,k) * xmb(i) * .001 * dt2 + endif + endif + if(flg(i) .and. k < ktcon(i)) then + evef = edt(i) * evfact + if(islimsk(i) == 1) evef=edt(i) * evfactl +! if(islimsk(i) == 1) evef=.07 +! if(islimsk(i) == 1) evef = 0. + qcond(i) = evef * (q1(i,k) - qeso(i,k)) & + & / (1. + el2orc * qeso(i,k) / t1(i,k)**2) + dp = 1000. * del(i,k) + if(rn(i) > 0. .and. qcond(i) < 0.) then + qevap(i) = -qcond(i) * (1.-exp(-.32*sqrt(dt2*rn(i)))) + qevap(i) = min(qevap(i), rn(i)*1000.*g/dp) + delq2(i) = delqev(i) + .001 * qevap(i) * dp / g + endif + if(rn(i) > 0. .and. qcond(i) < 0. .and. & + & delq2(i) > rntot(i)) then + qevap(i) = 1000.* g * (rntot(i) - delqev(i)) / dp + flg(i) = .false. + endif + if(rn(i) > 0. .and. qevap(i) > 0.) then + tem = .001 * dp / g + tem1 = qevap(i) * tem + if(tem1 > rn(i)) then + qevap(i) = rn(i) / tem + rn(i) = 0. + else + rn(i) = rn(i) - tem1 + endif + q1(i,k) = q1(i,k) + qevap(i) + t1(i,k) = t1(i,k) - elocp * qevap(i) + deltv(i) = - elocp*qevap(i)/dt2 + delq(i) = + qevap(i)/dt2 + delqev(i) = delqev(i) + .001*dp*qevap(i)/g + endif + delqbar(i) = delqbar(i) + delq(i)*dp/g + deltbar(i) = deltbar(i) + deltv(i)*dp/g + endif + endif + enddo + enddo +!j +! do i = 1, im +! if(me == 31 .and. cnvflg(i)) then +! if(cnvflg(i)) then +! print *, ' shallow delhbar, delqbar, deltbar = ', +! & delhbar(i),hvap*delqbar(i),cp*deltbar(i) +! print *, ' shallow delubar, delvbar = ',delubar(i),delvbar(i) +! print *, ' precip =', hvap*rn(i)*1000./dt2 +! print*,'pdif= ',pfld(i,kbcon(i))-pfld(i,ktcon(i)) +! endif +! enddo +! do n = 1, ntr +! do i = 1, im +! if(me == 31 .and. cnvflg(i)) then +! if(cnvflg(i)) then +! print *, ' tracer delebar = ',delebar(i,n) +! endif +! enddo +! enddo +!j + do i = 1, im + if(cnvflg(i)) then + if(rn(i) < 0. .or. .not.flg(i)) rn(i) = 0. + ktop(i) = ktcon(i) + kbot(i) = kbcon(i) + kcnv(i) = 2 + endif + enddo +! +! convective cloud water +! +!> - Calculate shallow convective cloud water. + do k = 1, km + do i = 1, im + if (cnvflg(i)) then + if (k >= kbcon(i) .and. k < ktcon(i)) then + cnvw(i,k) = cnvwt(i,k) * xmb(i) * dt2 + endif + endif + enddo + enddo + +! +! convective cloud cover +! +!> - Calculate convective cloud cover, which is used when pdf-based cloud fraction is used (i.e., pdfcld=.true.). + do k = 1, km + do i = 1, im + if (cnvflg(i)) then + if (k >= kbcon(i) .and. k < ktcon(i)) then + cnvc(i,k) = 0.04 * log(1. + 675. * eta(i,k) * xmb(i)) + cnvc(i,k) = min(cnvc(i,k), 0.2) + cnvc(i,k) = max(cnvc(i,k), 0.0) + endif + endif + enddo + enddo +! +! cloud water +! +!> - Separate detrained cloud water into liquid and ice species as a function of temperature only. + if (ncloud > 0) then +! + do k = 1, km1 + do i = 1, im + if (cnvflg(i)) then +! if (k > kb(i) .and. k <= ktcon(i)) then + if (k >= kbcon(i) .and. k <= ktcon(i)) then + tem = dellal(i,k) * xmb(i) * dt2 + tem1 = max(0.0, min(1.0, (tcr-t1(i,k))*tcrf)) + if (qtr(i,k,2) > -999.0) then + qtr(i,k,1) = qtr(i,k,1) + tem * tem1 ! ice + qtr(i,k,2) = qtr(i,k,2) + tem *(1.0-tem1) ! water + else + qtr(i,k,1) = qtr(i,k,1) + tem + endif + endif + endif + enddo + enddo +! + endif +!> - Store aerosol concentrations if present + if (do_aerosols) then + do n = 1, ntc + kk = n + itc - 1 + do k = 1, km + do i = 1, im + if(cnvflg(i) .and. rn(i) > 0.) then + if (k <= kmax(i)) qtr(i,k,kk) = qaero(i,k,n) + endif + enddo + enddo + enddo + endif +! +! hchuang code change +! +!> - Calculate and retain the updraft mass flux for dust transport by cumulus convection. +! +!> - Calculate the updraft convective mass flux. + do k = 1, km + do i = 1, im + if(cnvflg(i)) then + if(k >= kb(i) .and. k < ktop(i)) then + ud_mf(i,k) = eta(i,k) * xmb(i) * dt2 + endif + endif + enddo + enddo +!> - save the updraft convective mass flux at cloud top. + do i = 1, im + if(cnvflg(i)) then + k = ktop(i)-1 + dt_mf(i,k) = ud_mf(i,k) + endif + enddo +! +! include TKE contribution from shallow convection +! + if (ntk > 0) then +! + do k = 2, km1 + do i = 1, im + if(cnvflg(i)) then + if(k > kb(i) .and. k < ktop(i)) then + tem = 0.5 * (eta(i,k-1) + eta(i,k)) * xmb(i) + tem1 = pfld(i,k) * 100. / (rd * t1(i,k)) + sigmagfm(i) = max(sigmagfm(i), betaw) + ptem = tem / (sigmagfm(i) * tem1) + qtr(i,k,ntk)=qtr(i,k,ntk)+0.5*sigmagfm(i)*ptem*ptem + endif + endif + enddo + enddo +! + endif +!! + return + end diff --git a/projects2020/group05/tests/fortran/samfshalconv.xml b/projects2020/group05/tests/fortran/samfshalconv.xml new file mode 100644 index 00000000..e21e5b10 --- /dev/null +++ b/projects2020/group05/tests/fortran/samfshalconv.xml @@ -0,0 +1,59283 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + +
+ + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + +
+ + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + +
+ + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + +
+ + +
+ + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + +
+ +
+
diff --git a/projects2020/group05/tests/fortran/samfshalconv_benchmark.f90 b/projects2020/group05/tests/fortran/samfshalconv_benchmark.f90 new file mode 100644 index 00000000..e6d6c110 --- /dev/null +++ b/projects2020/group05/tests/fortran/samfshalconv_benchmark.f90 @@ -0,0 +1,1798 @@ +module samfshalconv_benchmark + real(kind=8),parameter:: grav =9.80665e+0_8 + real(kind=8),parameter:: cp =1.0046e+3_8 + real(kind=8),parameter:: hvap =2.5000e+6_8 + real(kind=8),parameter:: rv =4.6150e+2_8 + real(kind=8),parameter:: rd =2.8705e+2_8 + real(kind=8),parameter:: fv =rv/rd-1. + real(kind=8),parameter:: t0c =2.7315e+2_8 + real(kind=8),parameter:: cvap =1.8460e+3_8 + real(kind=8),parameter:: cliq =4.1855e+3_8 + real(kind=8),parameter:: eps =rd/rv + real(kind=8),parameter:: epsm1 =rd/rv-1. + + real(kind=8),parameter:: ttp =2.7316e+2_8 + real(kind=8),parameter:: csol =2.1060e+3_8 + real(kind=8),parameter:: hfus =3.3358e+5_8 + real(kind=8),parameter:: psat =6.1078e+2_8 + + contains + elemental function fpvs(t) + implicit none + real(kind=8) fpvs + real(kind=8),intent(in):: t + real(kind=8),parameter:: tliq=ttp + real(kind=8),parameter:: tice=ttp-20.0 + real(kind=8),parameter:: dldtl=cvap-cliq + real(kind=8),parameter:: heatl=hvap + real(kind=8),parameter:: xponal=-dldtl/rv + real(kind=8),parameter:: xponbl=-dldtl/rv+heatl/(rv*ttp) + real(kind=8),parameter:: dldti=cvap-csol + real(kind=8),parameter:: heati=hvap+hfus + real(kind=8),parameter:: xponai=-dldti/rv + real(kind=8),parameter:: xponbi=-dldti/rv+heati/(rv*ttp) + real(kind=8) tr,w,pvl,pvi +! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + tr=ttp/t + if(t.ge.tliq) then + fpvs=psat*(tr**xponal)*exp(xponbl*(1.-tr)) + elseif(t.lt.tice) then + fpvs=psat*(tr**xponai)*exp(xponbi*(1.-tr)) + else + w=(t-tice)/(tliq-tice) + pvl=psat*(tr**xponal)*exp(xponbl*(1.-tr)) + pvi=psat*(tr**xponai)*exp(xponbi*(1.-tr)) + fpvs=w*pvl+(1.-w)*pvi + endif +! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + end function + subroutine samfshalcnv(im,ix,km,delt,itc,ntc,ntk,ntr,delp, & + & prslp,psp,phil,qtr,q1,t1,u1,v1, & + & rn,kbot,ktop,kcnv,islimsk,garea, & + & dot,ncloud,hpbl,ud_mf,dt_mf,cnvw,cnvc, & + & clam,c0s,c1,pgcon,asolfac) +! + implicit none +! + integer, intent(inout) :: im, ix, km, itc, ntc, ntk, ntr, ncloud + integer, intent(inout) :: islimsk(im) + real(kind=8), intent(inout) :: delt + real(kind=8), intent(inout) :: psp(im), delp(ix,km), & + & prslp(ix,km), garea(im), hpbl(im), dot(ix,km), phil(ix,km) +! + integer, intent(inout) :: kcnv(im) + real(kind=8), intent(inout) :: qtr(ix,km,ntr+2), & + & q1(ix,km), t1(ix,km), u1(ix,km), v1(ix,km) +! + integer, intent(inout) :: kbot(im), ktop(im) + real(kind=8), intent(inout) :: rn(im), & + & cnvw(ix,km), cnvc(ix,km), ud_mf(im,km), dt_mf(im,km) +! + real(kind=8), intent(inout) :: clam, c0s, c1, & + & asolfac, pgcon +! +! local variables + integer i,j,indx, k, kk, km1, n + integer kpbl(im) +! + real(kind=8) clamd, tkemx, tkemn, dtke +! + real(kind=8) dellat, delta, & + & c0l, d0, & + & desdt, dp, & + & dq, dqsdp, dqsdt, dt, & + & dt2, dtmax, dtmin, dxcrt, & + & dv1h, dv2h, dv3h, & + & dv1q, dv2q, dv3q, & + & dz, dz1, e1, & + & el2orc, elocp, aafac, cm, & + & es, etah, h1, & + & evef, evfact, evfactl, fact1, & + & fact2, factor, dthk, & + & g, gamma, pprime, betaw, & + & qlk, qrch, qs, & + & rfact, shear, tfac, & + & val, val1, val2, & + & w1, w1l, w1s, w2, & + & w2l, w2s, w3, w3l, & + & w3s, w4, w4l, w4s, & + & rho, tem, tem1, tem2, & + & ptem, ptem1 +! + integer kb(im), kbcon(im), kbcon1(im), & + & ktcon(im), ktcon1(im), ktconn(im), & + & kbm(im), kmax(im) +! + real(kind=8) aa1(im), cina(im), & + & tkemean(im), clamt(im), & + & ps(im), del(ix,km), prsl(ix,km), & + & umean(im), tauadv(im), gdx(im), & + & delhbar(im), delq(im), delq2(im), & + & delqbar(im), delqev(im), deltbar(im), & + & deltv(im), dtconv(im), edt(im), & + & pdot(im), po(im,km), & + & qcond(im), qevap(im), hmax(im), & + & rntot(im), vshear(im), & + & xlamud(im), xmb(im), xmbmax(im), & + & delebar(im,ntr), & + & delubar(im), delvbar(im) +! + real(kind=8) c0(im) +! + real(kind=8) crtlamd +! + real(kind=8) cinpcr, cinpcrmx, cinpcrmn, & + & cinacr, cinacrmx, cinacrmn +! +! parameters for updraft velocity calculation + real(kind=8) bet1, cd1, f1, gam1, & + & bb1, bb2 +! & bb1, bb2, wucb +!c +! physical parameters +! parameter(g=grav,asolfac=0.89) + parameter(g=grav) + parameter(elocp=hvap/cp, & + & el2orc=hvap*hvap/(rv*cp)) +! parameter(c0s=0.002,c1=5.e-4,d0=.01) +! parameter(d0=.01) + parameter(d0=.001) +! parameter(c0l=c0s*asolfac) +! +! asolfac: aerosol-aware parameter based on Lim & Hong (2012) +! asolfac= cx / c0s(=.002) +! cx = min([-0.7 ln(Nccn) + 24]*1.e-4, c0s) +! Nccn: CCN number concentration in cm^(-3) +! Until a realistic Nccn is provided, Nccns are assumed +! as Nccn=100 for sea and Nccn=1000 for land +! + parameter(cm=1.0,delta=fv) + parameter(fact1=(cvap-cliq)/rv,fact2=hvap/rv-fact1*t0c) + parameter(clamd=0.1,tkemx=0.65,tkemn=0.05) + parameter(dtke=tkemx-tkemn) + parameter(dthk=25.) + parameter(cinpcrmx=180.,cinpcrmn=120.) +! parameter(cinacrmx=-120.,cinacrmn=-120.) + parameter(cinacrmx=-120.,cinacrmn=-80.) + parameter(crtlamd=3.e-4) + parameter(dtmax=10800.,dtmin=600.) + parameter(bet1=1.875,cd1=.506,f1=2.0,gam1=.5) + parameter(betaw=.03,dxcrt=15.e3) + parameter(h1=0.33333333) +! local variables and arrays + real(kind=8) pfld(im,km), to(im,km), qo(im,km), & + & uo(im,km), vo(im,km), qeso(im,km), & + & ctr(im,km,ntr), ctro(im,km,ntr) +! for aerosol transport + real(kind=8) qaero(im,km,ntc) +! for updraft velocity calculation + real(kind=8) wu2(im,km), buo(im,km), drag(im,km) + real(kind=8) wc(im), scaldfunc(im), sigmagfm(im) +! +! cloud water +! real(kind=8) qlko_ktcon(im), dellal(im,km), tvo(im,km), + real(kind=8) qlko_ktcon(im), dellal(im,km), & + & dbyo(im,km), zo(im,km), xlamue(im,km), & + & heo(im,km), heso(im,km), & + & dellah(im,km), dellaq(im,km), & + & dellae(im,km,ntr), & + & dellau(im,km), dellav(im,km), hcko(im,km), & + & ucko(im,km), vcko(im,km), qcko(im,km), & + & qrcko(im,km), ecko(im,km,ntr), & + & eta(im,km), & + & zi(im,km), pwo(im,km), c0t(im,km), & + & sumx(im), tx1(im), cnvwt(im,km) +! + logical do_aerosols, totflg, cnvflg(im), flg(im) +! + real(kind=8) tf, tcr, tcrf + parameter (tf=233.16, tcr=263.16, tcrf=1.0/(tcr-tf)) + +!------------------SERIALIZATION--------------------------------------- + !ser init directory='/data_out' prefix='Serialized' prefix_ref='SavedData' +! +!----------------------------------------------------------------------- +!> ## Determine whether to perform aerosol transport + do_aerosols = (itc > 0) .and. (ntc > 0) .and. (ntr > 0) + if (do_aerosols) do_aerosols = (ntr >= itc + ntc - 3) +! +!************************************************************************ +! convert input Pa terms to Cb terms -- Moorthi +!> ## Compute preliminary quantities needed for the static and feedback control portions of the algorithm. +!> - Convert input pressure terms to centibar units. + ps = psp * 0.001 + prsl = prslp * 0.001 + del = delp * 0.001 +!************************************************************************ +! + km1 = km - 1 +! +! initialize arrays +! +!> - Initialize column-integrated and other single-value-per-column variable arrays. + do i=1,im + cnvflg(i) = .true. + if(kcnv(i) == 1) cnvflg(i) = .false. + if(cnvflg(i)) then + kbot(i)=km+1 + ktop(i)=0 + endif + rn(i)=0. + kbcon(i)=km + ktcon(i)=1 + ktconn(i)=1 + kb(i)=km + pdot(i) = 0. + qlko_ktcon(i) = 0. + edt(i) = 0. + aa1(i) = 0. + cina(i) = 0. + vshear(i) = 0. + gdx(i) = sqrt(garea(i)) + enddo +!! +!> - Return to the calling routine if deep convection is present or the surface buoyancy flux is negative. + totflg = .true. + do i=1,im + totflg = totflg .and. (.not. cnvflg(i)) + enddo + if(totflg) return +!! +!> - determine aerosol-aware rain conversion parameter over land + do i=1,im + if(islimsk(i) == 1) then + c0(i) = c0s*asolfac + else + c0(i) = c0s + endif + enddo +! +!> - determine rain conversion parameter above the freezing level which exponentially decreases with decreasing temperature from Han et al.'s (2017) \cite han_et_al_2017 equation 8. + do k = 1, km + do i = 1, im + if(t1(i,k) > 273.16) then + c0t(i,k) = c0(i) + else + tem = d0 * (t1(i,k) - 273.16) + tem1 = exp(tem) + c0t(i,k) = c0(i) * tem1 + endif + enddo + enddo +! +!> - Initialize convective cloud water and cloud cover to zero. + do k = 1, km + do i = 1, im + cnvw(i,k) = 0. + cnvc(i,k) = 0. + enddo + enddo +! hchuang code change +!> - Initialize updraft mass fluxes to zero. + do k = 1, km + do i = 1, im + ud_mf(i,k) = 0. + dt_mf(i,k) = 0. + enddo + enddo +! + dt2 = delt +! +! model tunable parameters are all here +! clam = .3 +! aafac = .1 + aafac = .05 +! evef = 0.07 + evfact = 0.3 + evfactl = 0.3 +! +! pgcon = 0.7 ! Gregory et al. (1997, QJRMS) +! pgcon = 0.55 ! Zhang & Wu (2003,JAS) + w1l = -8.e-3 + w2l = -4.e-2 + w3l = -5.e-3 + w4l = -5.e-4 + w1s = -2.e-4 + w2s = -2.e-3 + w3s = -1.e-3 + w4s = -2.e-5 +! +! define top layer for search of the downdraft originating layer +! and the maximum thetae for updraft +! +!> - Determine maximum indices for the parcel starting point (kbm) and cloud top (kmax). + do i=1,im + kbm(i) = km + kmax(i) = km + tx1(i) = 1.0 / ps(i) + enddo +! + do k = 1, km + do i=1,im + if (prsl(i,k)*tx1(i) > 0.70) kbm(i) = k + 1 + if (prsl(i,k)*tx1(i) > 0.60) kmax(i) = k + 1 + enddo + enddo + do i=1,im + kbm(i) = min(kbm(i),kmax(i)) + enddo +! +! hydrostatic height assume zero terr and compute +! updraft entrainment rate as an inverse function of height +! +!> - Calculate hydrostatic height at layer centers assuming a flat surface (no terrain) from the geopotential. + do k = 1, km + do i=1,im + zo(i,k) = phil(i,k) / g + enddo + enddo +!> - Calculate interface height + do k = 1, km1 + do i=1,im + zi(i,k) = 0.5*(zo(i,k)+zo(i,k+1)) + enddo + enddo +! +! pbl height +! +!> - Find the index for the PBL top using the PBL height; enforce that it is lower than the maximum parcel starting level. + do i=1,im + flg(i) = cnvflg(i) + kpbl(i)= 1 + enddo + do k = 2, km1 + do i=1,im + if (flg(i) .and. zo(i,k) <= hpbl(i)) then + kpbl(i) = k + else + flg(i) = .false. + endif + enddo + enddo + do i=1,im + kpbl(i)= min(kpbl(i),kbm(i)) + enddo +! +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +! convert surface pressure to mb from cb +! +!> - Convert prsl from centibar to millibar, set normalized mass flux to 1, cloud properties to 0, and save model state variables (after advection/turbulence). + do k = 1, km + do i = 1, im + if (cnvflg(i) .and. k <= kmax(i)) then + pfld(i,k) = prsl(i,k) * 10.0 + eta(i,k) = 1. + hcko(i,k) = 0. + qcko(i,k) = 0. + qrcko(i,k)= 0. + ucko(i,k) = 0. + vcko(i,k) = 0. + dbyo(i,k) = 0. + pwo(i,k) = 0. + dellal(i,k) = 0. + to(i,k) = t1(i,k) + qo(i,k) = q1(i,k) + uo(i,k) = u1(i,k) + vo(i,k) = v1(i,k) +! uo(i,k) = u1(i,k) * rcs(i) +! vo(i,k) = v1(i,k) * rcs(i) + wu2(i,k) = 0. + buo(i,k) = 0. + drag(i,k) = 0. + cnvwt(i,k) = 0. + endif + enddo + enddo +! +! initialize tracer variables +! + do n = 3, ntr+2 + kk = n-2 + do k = 1, km + do i = 1, im + if (cnvflg(i) .and. k <= kmax(i)) then + ctr(i,k,kk) = qtr(i,k,n) + ctro(i,k,kk) = qtr(i,k,n) + ecko(i,k,kk) = 0. + endif + enddo + enddo + enddo +!> - Calculate saturation specific humidity and enforce minimum moisture values. + do k = 1, km + do i=1,im + if (cnvflg(i) .and. k <= kmax(i)) then + qeso(i,k) = 0.01 * fpvs(to(i,k)) ! fpvs is in pa + qeso(i,k) = eps * qeso(i,k) / (pfld(i,k) + epsm1*qeso(i,k)) + val1 = 1.e-8 + qeso(i,k) = max(qeso(i,k), val1) + val2 = 1.e-10 + qo(i,k) = max(qo(i,k), val2 ) +! qo(i,k) = min(qo(i,k),qeso(i,k)) +! tvo(i,k) = to(i,k) + delta * to(i,k) * qo(i,k) + endif + enddo + enddo +! +! compute moist static energy +! +!> - Calculate moist static energy (heo) and saturation moist static energy (heso). + do k = 1, km + do i=1,im + if (cnvflg(i) .and. k <= kmax(i)) then +! tem = g * zo(i,k) + cp * to(i,k) + tem = phil(i,k) + cp * to(i,k) + heo(i,k) = tem + hvap * qo(i,k) + heso(i,k) = tem + hvap * qeso(i,k) +! heo(i,k) = min(heo(i,k),heso(i,k)) + endif + enddo + enddo + +! +! determine level with largest moist static energy within pbl +! this is the level where updraft starts +! +!> ## Perform calculations related to the updraft of the entraining/detraining cloud model ("static control"). +!> - Search in the PBL for the level of maximum moist static energy to start the ascending parcel. + do i=1,im + if (cnvflg(i)) then + hmax(i) = heo(i,1) + kb(i) = 1 + endif + enddo + do k = 2, km + do i=1,im + if (cnvflg(i) .and. k <= kpbl(i)) then + if(heo(i,k) > hmax(i)) then + kb(i) = k + hmax(i) = heo(i,k) + endif + endif + enddo + enddo + +! +!> - Calculate the temperature, water vapor mixing ratio, and pressure at interface levels. + do k = 1, km1 + do i=1,im + if (cnvflg(i) .and. k <= kmax(i)-1) then + dz = .5 * (zo(i,k+1) - zo(i,k)) + dp = .5 * (pfld(i,k+1) - pfld(i,k)) + es = 0.01 * fpvs(to(i,k+1)) ! fpvs is in pa + pprime = pfld(i,k+1) + epsm1 * es + qs = eps * es / pprime + dqsdp = - qs / pprime + desdt = es * (fact1 / to(i,k+1) + fact2 / (to(i,k+1)**2)) + dqsdt = qs * pfld(i,k+1) * desdt / (es * pprime) + gamma = el2orc * qeso(i,k+1) / (to(i,k+1)**2) + dt = (g * dz + hvap * dqsdp * dp) / (cp * (1. + gamma)) + dq = dqsdt * dt + dqsdp * dp + to(i,k) = to(i,k+1) + dt + qo(i,k) = qo(i,k+1) + dq + po(i,k) = .5 * (pfld(i,k) + pfld(i,k+1)) + endif + enddo + enddo +! +!> - Recalculate saturation specific humidity, moist static energy, saturation moist static energy, and horizontal momentum on interface levels. Enforce minimum specific humidity. + do k = 1, km1 + do i=1,im + if (cnvflg(i) .and. k <= kmax(i)-1) then + qeso(i,k) = 0.01 * fpvs(to(i,k)) ! fpvs is in pa + qeso(i,k) = eps * qeso(i,k) / (po(i,k) + epsm1*qeso(i,k)) + val1 = 1.e-8 + qeso(i,k) = max(qeso(i,k), val1) + val2 = 1.e-10 + qo(i,k) = max(qo(i,k), val2 ) +! qo(i,k) = min(qo(i,k),qeso(i,k)) + heo(i,k) = .5 * g * (zo(i,k) + zo(i,k+1)) + & + & cp * to(i,k) + hvap * qo(i,k) + heso(i,k) = .5 * g * (zo(i,k) + zo(i,k+1)) + & + & cp * to(i,k) + hvap * qeso(i,k) + uo(i,k) = .5 * (uo(i,k) + uo(i,k+1)) + vo(i,k) = .5 * (vo(i,k) + vo(i,k+1)) + endif + enddo + enddo + do n = 1, ntr + do k = 1, km1 + do i=1,im + if (cnvflg(i) .and. k <= kmax(i)-1) then + ctro(i,k,n) = .5 * (ctro(i,k,n) + ctro(i,k+1,n)) + endif + enddo + enddo + enddo + +! +! look for the level of free convection as cloud base +! +!> - Search below the index "kbm" for the level of free convection (LFC) where the condition \f$h_b > h^*\f$ is first met, where \f$h_b, h^*\f$ are the state moist static energy at the parcel's starting level and saturation moist static energy, respectively. Set "kbcon" to the index of the LFC. + do i=1,im + flg(i) = cnvflg(i) + if(flg(i)) kbcon(i) = kmax(i) + enddo + do k = 2, km1 + do i=1,im + if (flg(i) .and. k < kbm(i)) then + if(k > kb(i) .and. heo(i,kb(i)) > heso(i,k)) then + kbcon(i) = k + flg(i) = .false. + endif + endif + enddo + enddo +! + do i=1,im + if(cnvflg(i)) then + if(kbcon(i) == kmax(i)) cnvflg(i) = .false. + endif + enddo +!! +!> - If no LFC, return to the calling routine without modifying state variables. + totflg = .true. + do i=1,im + totflg = totflg .and. (.not. cnvflg(i)) + enddo + if(totflg) return +!! +!> - Determine the vertical pressure velocity at the LFC. After Han and Pan (2011) \cite han_and_pan_2011 , determine the maximum pressure thickness between a parcel's starting level and the LFC. If a parcel doesn't reach the LFC within the critical thickness, then the convective inhibition is deemed too great for convection to be triggered, and the subroutine returns to the calling routine without modifying the state variables. + do i=1,im + if(cnvflg(i)) then +! pdot(i) = 10.* dot(i,kbcon(i)) + pdot(i) = 0.01 * dot(i,kbcon(i)) ! Now dot is in Pa/s + endif + enddo +! +! turn off convection if pressure depth between parcel source level +! and cloud base is larger than a critical value, cinpcr +! + do i=1,im + if(cnvflg(i)) then + if(islimsk(i) == 1) then + w1 = w1l + w2 = w2l + w3 = w3l + w4 = w4l + else + w1 = w1s + w2 = w2s + w3 = w3s + w4 = w4s + endif + if(pdot(i) <= w4) then + tem = (pdot(i) - w4) / (w3 - w4) + elseif(pdot(i) >= -w4) then + tem = - (pdot(i) + w4) / (w4 - w3) + else + tem = 0. + endif + val1 = -1. + tem = max(tem,val1) + val2 = 1. + tem = min(tem,val2) + ptem = 1. - tem + ptem1= .5*(cinpcrmx-cinpcrmn) + cinpcr = cinpcrmx - ptem * ptem1 + tem1 = pfld(i,kb(i)) - pfld(i,kbcon(i)) + if(tem1 > cinpcr) then + cnvflg(i) = .false. + endif + endif + enddo + + +!! + totflg = .true. + do i=1,im + totflg = totflg .and. (.not. cnvflg(i)) + enddo + if(totflg) return +! +! turbulent entrainment rate assumed to be proportional +! to subcloud mean TKE +! + if(ntk > 0) then +! + do i= 1, im + if(cnvflg(i)) then + sumx(i) = 0. + tkemean(i) = 0. + endif + enddo + do k = 1, km1 + do i = 1, im + if(cnvflg(i)) then + if(k >= kb(i) .and. k < kbcon(i)) then + dz = zo(i,k+1) - zo(i,k) + tem = 0.5 * (qtr(i,k,ntk)+qtr(i,k+1,ntk)) + tkemean(i) = tkemean(i) + tem * dz + sumx(i) = sumx(i) + dz + endif + endif + enddo + enddo +! + do i= 1, im + if(cnvflg(i)) then + tkemean(i) = tkemean(i) / sumx(i) + if(tkemean(i) > tkemx) then + clamt(i) = clam + clamd + else if(tkemean(i) < tkemn) then + clamt(i) = clam - clamd + else + tem = tkemx - tkemean(i) + tem1 = 1. - 2. * tem / dtke + clamt(i) = clam + clamd * tem1 + endif + endif + enddo +! + else +! + do i= 1, im + if(cnvflg(i)) then + clamt(i) = clam + endif + enddo +! + endif +!! +! +! assume updraft entrainment rate +! is an inverse function of height +! + do k = 1, km1 + do i=1,im + if(cnvflg(i)) then + xlamue(i,k) = clamt(i) / zi(i,k) + endif + enddo + enddo + do i=1,im + if(cnvflg(i)) then + xlamue(i,km) = xlamue(i,km1) + endif + enddo +! +! specify the detrainment rate for the updrafts +! +!! (The updraft detrainment rate is set constant and equal to the entrainment rate at cloud base.) +!! +!> - The updraft detrainment rate is vertically constant and proportional to clamt + do i = 1, im + if(cnvflg(i)) then +! xlamud(i) = xlamue(i,kbcon(i)) +! xlamud(i) = crtlamd + xlamud(i) = 0.001 * clamt(i) + endif + enddo +! +! determine updraft mass flux for the subcloud layers +! +!> - Calculate the normalized mass flux for subcloud and in-cloud layers according to Pan and Wu (1995) \cite pan_and_wu_1995 equation 1: +!! \f[ +!! \frac{1}{\eta}\frac{\partial \eta}{\partial z} = \lambda_e - \lambda_d +!! \f] +!! where \f$\eta\f$ is the normalized mass flux, \f$\lambda_e\f$ is the entrainment rate and \f$\lambda_d\f$ is the detrainment rate. The normalized mass flux increases upward below the cloud base and decreases upward above. + do k = km1, 1, -1 + do i = 1, im + if (cnvflg(i)) then + if(k < kbcon(i) .and. k >= kb(i)) then + dz = zi(i,k+1) - zi(i,k) + ptem = 0.5*(xlamue(i,k)+xlamue(i,k+1))-xlamud(i) + eta(i,k) = eta(i,k+1) / (1. + ptem * dz) + endif + endif + enddo + enddo +! +! compute mass flux above cloud base +! + do i = 1, im + flg(i) = cnvflg(i) + enddo + do k = 2, km1 + do i = 1, im + if(flg(i))then + if(k > kbcon(i) .and. k < kmax(i)) then + dz = zi(i,k) - zi(i,k-1) + ptem = 0.5*(xlamue(i,k)+xlamue(i,k-1))-xlamud(i) + eta(i,k) = eta(i,k-1) * (1 + ptem * dz) + if(eta(i,k) <= 0.) then + kmax(i) = k + ktconn(i) = k + kbm(i) = min(kbm(i),kmax(i)) + flg(i) = .false. + endif + endif + endif + enddo + enddo +! +! compute updraft cloud property +! +!> - Set cloud properties equal to the state variables at updraft starting level (kb). + do i = 1, im + if(cnvflg(i)) then + indx = kb(i) + hcko(i,indx) = heo(i,indx) + ucko(i,indx) = uo(i,indx) + vcko(i,indx) = vo(i,indx) + endif + enddo +! for tracers + do n = 1, ntr + do i = 1, im + if(cnvflg(i)) then + indx = kb(i) + ecko(i,indx,n) = ctro(i,indx,n) + endif + enddo + enddo + +! +! cm is an enhancement factor in entrainment rates for momentum +! +!> - Calculate the cloud properties as a parcel ascends, modified by entrainment and detrainment. Discretization follows Appendix B of Grell (1993) \cite grell_1993 . Following Han and Pan (2006) \cite han_and_pan_2006, the convective momentum transport is reduced by the convection-induced pressure gradient force by the constant "pgcon", currently set to 0.55 after Zhang and Wu (2003) \cite zhang_and_wu_2003 . + do k = 2, km1 + do i = 1, im + if (cnvflg(i)) then + if(k > kb(i) .and. k < kmax(i)) then + dz = zi(i,k) - zi(i,k-1) + tem = 0.5 * (xlamue(i,k)+xlamue(i,k-1)) * dz + tem1 = 0.5 * xlamud(i) * dz + factor = 1. + tem - tem1 + hcko(i,k) = ((1.-tem1)*hcko(i,k-1)+tem*0.5* & + & (heo(i,k)+heo(i,k-1)))/factor + dbyo(i,k) = hcko(i,k) - heso(i,k) +! + tem = 0.5 * cm * tem + factor = 1. + tem + ptem = tem + pgcon + ptem1= tem - pgcon + ucko(i,k) = ((1.-tem)*ucko(i,k-1)+ptem*uo(i,k) & + & +ptem1*uo(i,k-1))/factor + vcko(i,k) = ((1.-tem)*vcko(i,k-1)+ptem*vo(i,k) & + & +ptem1*vo(i,k-1))/factor + endif + endif + enddo + enddo + do n = 1, ntr + do k = 2, km1 + do i = 1, im + if (cnvflg(i)) then + if(k > kb(i) .and. k < kmax(i)) then + dz = zi(i,k) - zi(i,k-1) + tem = 0.25 * (xlamue(i,k)+xlamue(i,k-1)) * dz + factor = 1. + tem + ecko(i,k,n) = ((1.-tem)*ecko(i,k-1,n)+tem* & + & (ctro(i,k,n)+ctro(i,k-1,n)))/factor + endif + endif + enddo + enddo + enddo + +! +! taking account into convection inhibition due to existence of +! dry layers below cloud base +! +!> - With entrainment, recalculate the LFC as the first level where buoyancy is positive. The difference in pressure levels between LFCs calculated with/without entrainment must be less than a threshold (currently 25 hPa). Otherwise, convection is inhibited and the scheme returns to the calling routine without modifying the state variables. This is the subcloud dryness trigger modification discussed in Han and Pan (2011) \cite han_and_pan_2011. + do i=1,im + flg(i) = cnvflg(i) + kbcon1(i) = kmax(i) + enddo + do k = 2, km1 + do i=1,im + if (flg(i) .and. k < kbm(i)) then + if(k >= kbcon(i) .and. dbyo(i,k) > 0.) then + kbcon1(i) = k + flg(i) = .false. + endif + endif + enddo + enddo + do i=1,im + if(cnvflg(i)) then + if(kbcon1(i) == kmax(i)) cnvflg(i) = .false. + endif + enddo + do i=1,im + if(cnvflg(i)) then + tem = pfld(i,kbcon(i)) - pfld(i,kbcon1(i)) + if(tem > dthk) then + cnvflg(i) = .false. + endif + endif + enddo + +!! + totflg = .true. + do i = 1, im + totflg = totflg .and. (.not. cnvflg(i)) + enddo + if(totflg) return +!! +! +! calculate convective inhibition +! +!> - Calculate additional trigger condition of the convective inhibition (CIN) according to Han et al.'s (2017) \cite han_et_al_2017 equation 13. + do k = 2, km1 + do i = 1, im + if (cnvflg(i)) then + if(k > kb(i) .and. k < kbcon1(i)) then + dz1 = zo(i,k+1) - zo(i,k) + gamma = el2orc * qeso(i,k) / (to(i,k)**2) + rfact = 1. + delta * cp * gamma & + & * to(i,k) / hvap + cina(i) = cina(i) + & +! & dz1 * eta(i,k) * (g / (cp * to(i,k))) + & dz1 * (g / (cp * to(i,k))) & + & * dbyo(i,k) / (1. + gamma) & + & * rfact + val = 0. + cina(i) = cina(i) + & +! & dz1 * eta(i,k) * g * delta * + & dz1 * g * delta * & + & max(val,(qeso(i,k) - qo(i,k))) + endif + endif + enddo + enddo +!> - Turn off convection if the CIN is less than a critical value (cinacr) which is inversely proportional to the large-scale vertical velocity. + do i = 1, im + if(cnvflg(i)) then +! + if(islimsk(i) == 1) then + w1 = w1l + w2 = w2l + w3 = w3l + w4 = w4l + else + w1 = w1s + w2 = w2s + w3 = w3s + w4 = w4s + endif + if(pdot(i) <= w4) then + tem = (pdot(i) - w4) / (w3 - w4) + elseif(pdot(i) >= -w4) then + tem = - (pdot(i) + w4) / (w4 - w3) + else + tem = 0. + endif + + val1 = -1. + tem = max(tem,val1) + val2 = 1. + tem = min(tem,val2) + tem = 1. - tem + tem1= .5*(cinacrmx-cinacrmn) + cinacr = cinacrmx - tem * tem1 +! +! cinacr = cinacrmx + if(cina(i) < cinacr) cnvflg(i) = .false. + endif + enddo + +!! + totflg = .true. + do i=1,im + totflg = totflg .and. (.not. cnvflg(i)) + enddo + if(totflg) return +!! +! +! determine first guess cloud top as the level of zero buoyancy +! limited to the level of P/Ps=0.7 +! +!> - Calculate the cloud top as the first level where parcel buoyancy becomes negative; the maximum possible value is at \f$p=0.7p_{sfc}\f$. + do i = 1, im + flg(i) = cnvflg(i) + if(flg(i)) ktcon(i) = kbm(i) + enddo + do k = 2, km1 + do i=1,im + if (flg(i) .and. k < kbm(i)) then + if(k > kbcon1(i) .and. dbyo(i,k) < 0.) then + ktcon(i) = k + flg(i) = .false. + endif + endif + enddo + enddo +! +! specify upper limit of mass flux at cloud base +! +!> - Calculate the maximum value of the cloud base mass flux using the CFL-criterion-based formula of Han and Pan (2011) \cite han_and_pan_2011, equation 7. + do i = 1, im + if(cnvflg(i)) then +! xmbmax(i) = .1 +! + k = kbcon(i) + dp = 1000. * del(i,k) + xmbmax(i) = dp / (2. * g * dt2) +! +! xmbmax(i) = dp / (g * dt2) +! +! tem = dp / (g * dt2) +! xmbmax(i) = min(tem, xmbmax(i)) + endif + enddo +! +! compute cloud moisture property and precipitation +! +!> - Set cloud moisture property equal to the enviromental moisture at updraft starting level (kb). + do i = 1, im + if (cnvflg(i)) then + aa1(i) = 0. + qcko(i,kb(i)) = qo(i,kb(i)) + qrcko(i,kb(i)) = qo(i,kb(i)) + endif + enddo +!> - Calculate the moisture content of the entraining/detraining parcel (qcko) and the value it would have if just saturated (qrch), according to equation A.14 in Grell (1993) \cite grell_1993 . Their difference is the amount of convective cloud water (qlk = rain + condensate). Determine the portion of convective cloud water that remains suspended and the portion that is converted into convective precipitation (pwo). Calculate and save the negative cloud work function (aa1) due to water loading. Above the level of minimum moist static energy, some of the cloud water is detrained into the grid-scale cloud water from every cloud layer with a rate of 0.0005 \f$m^{-1}\f$ (dellal). + do k = 2, km1 + do i = 1, im + if (cnvflg(i)) then + if(k > kb(i) .and. k < ktcon(i)) then + dz = zi(i,k) - zi(i,k-1) + gamma = el2orc * qeso(i,k) / (to(i,k)**2) + qrch = qeso(i,k) & + & + gamma * dbyo(i,k) / (hvap * (1. + gamma)) +!j + tem = 0.5 * (xlamue(i,k)+xlamue(i,k-1)) * dz + tem1 = 0.5 * xlamud(i) * dz + factor = 1. + tem - tem1 + qcko(i,k) = ((1.-tem1)*qcko(i,k-1)+tem*0.5* & + & (qo(i,k)+qo(i,k-1)))/factor + qrcko(i,k) = qcko(i,k) +!j + dq = eta(i,k) * (qcko(i,k) - qrch) +! +! rhbar(i) = rhbar(i) + qo(i,k) / qeso(i,k) +! +! below lfc check if there is excess moisture to release latent heat +! + if(k >= kbcon(i) .and. dq > 0.) then + etah = .5 * (eta(i,k) + eta(i,k-1)) + dp = 1000. * del(i,k) + if(ncloud > 0) then + ptem = c0t(i,k) + c1 + qlk = dq / (eta(i,k) + etah * ptem * dz) + dellal(i,k) = etah * c1 * dz * qlk * g / dp + else + qlk = dq / (eta(i,k) + etah * c0t(i,k) * dz) + endif + buo(i,k) = buo(i,k) - g * qlk + qcko(i,k)= qlk + qrch + pwo(i,k) = etah * c0t(i,k) * dz * qlk + cnvwt(i,k) = etah * qlk * g / dp + endif +! +! compute buoyancy and drag for updraft velocity +! + if(k >= kbcon(i)) then + rfact = 1. + delta * cp * gamma & + & * to(i,k) / hvap + buo(i,k) = buo(i,k) + (g / (cp * to(i,k))) & + & * dbyo(i,k) / (1. + gamma) & + & * rfact + val = 0. + buo(i,k) = buo(i,k) + g * delta * & + & max(val,(qeso(i,k) - qo(i,k))) + drag(i,k) = max(xlamue(i,k),xlamud(i)) + endif +! + endif + endif + enddo + enddo +! +! calculate cloud work function +! +! do k = 2, km1 +! do i = 1, im +! if (cnvflg(i)) then +! if(k >= kbcon(i) .and. k < ktcon(i)) then +! dz1 = zo(i,k+1) - zo(i,k) +! gamma = el2orc * qeso(i,k) / (to(i,k)**2) +! rfact = 1. + delta * cp * gamma +! & * to(i,k) / hvap +! aa1(i) = aa1(i) + +!! & dz1 * eta(i,k) * (g / (cp * to(i,k))) +! & dz1 * (g / (cp * to(i,k))) +! & * dbyo(i,k) / (1. + gamma) +! & * rfact +! val = 0. +! aa1(i) = aa1(i) + +!! & dz1 * eta(i,k) * g * delta * +! & dz1 * g * delta * +! & max(val,(qeso(i,k) - qo(i,k))) +! endif +! endif +! enddo +! enddo +! do i = 1, im +! if(cnvflg(i) .and. aa1(i) <= 0.) cnvflg(i) = .false. +! enddo +! +! calculate cloud work function +! +!> - Calculate the cloud work function according to Pan and Wu (1995) \cite pan_and_wu_1995 equation 4: +!! \f[ +!! A_u=\int_{z_0}^{z_t}\frac{g}{c_pT(z)}\frac{\eta}{1 + \gamma}[h(z)-h^*(z)]dz +!! \f] +!! (discretized according to Grell (1993) \cite grell_1993 equation B.10 using B.2 and B.3 of Arakawa and Schubert (1974) \cite arakawa_and_schubert_1974 and assuming \f$\eta=1\f$) where \f$A_u\f$ is the updraft cloud work function, \f$z_0\f$ and \f$z_t\f$ are cloud base and cloud top, respectively, \f$\gamma = \frac{L}{c_p}\left(\frac{\partial \overline{q_s}}{\partial T}\right)_p\f$ and other quantities are previously defined. + do i = 1, im + if (cnvflg(i)) then + aa1(i) = 0. + endif + enddo + do k = 2, km1 + do i = 1, im + if (cnvflg(i)) then + if(k >= kbcon(i) .and. k < ktcon(i)) then + dz1 = zo(i,k+1) - zo(i,k) + aa1(i) = aa1(i) + buo(i,k) * dz1 + endif + endif + enddo + enddo + do i = 1, im + if(cnvflg(i) .and. aa1(i) <= 0.) cnvflg(i) = .false. + enddo +!! +!> - If the updraft cloud work function is negative, convection does not occur, and the scheme returns to the calling routine. + totflg = .true. + do i=1,im + totflg = totflg .and. (.not. cnvflg(i)) + enddo + if(totflg) return +!! +! +! estimate the onvective overshooting as the level +! where the [aafac * cloud work function] becomes zero, +! which is the final cloud top +! limited to the level of P/Ps=0.7 +! +!> - Continue calculating the cloud work function past the point of neutral buoyancy to represent overshooting according to Han and Pan (2011) \cite han_and_pan_2011 . Convective overshooting stops when \f$ cA_u < 0\f$ where \f$c\f$ is currently 10%, or when 10% of the updraft cloud work function has been consumed by the stable buoyancy force. Overshooting is also limited to the level where \f$p=0.7p_{sfc}\f$. + do i = 1, im + if (cnvflg(i)) then + aa1(i) = aafac * aa1(i) + endif + enddo +! + do i = 1, im + flg(i) = cnvflg(i) + ktcon1(i) = kbm(i) + enddo + do k = 2, km1 + do i = 1, im + if (flg(i)) then + if(k >= ktcon(i) .and. k < kbm(i)) then + dz1 = zo(i,k+1) - zo(i,k) + gamma = el2orc * qeso(i,k) / (to(i,k)**2) + rfact = 1. + delta * cp * gamma & + & * to(i,k) / hvap + aa1(i) = aa1(i) + & +! & dz1 * eta(i,k) * (g / (cp * to(i,k))) + & dz1 * (g / (cp * to(i,k))) & + & * dbyo(i,k) / (1. + gamma) & + & * rfact +! val = 0. +! aa1(i) = aa1(i) + +!! & dz1 * eta(i,k) * g * delta * +! & dz1 * g * delta * +! & max(val,(qeso(i,k) - qo(i,k))) + if(aa1(i) < 0.) then + ktcon1(i) = k + flg(i) = .false. + endif + endif + endif + enddo + enddo +! +! compute cloud moisture property, detraining cloud water +! and precipitation in overshooting layers +! +!> - For the overshooting convection, calculate the moisture content of the entraining/detraining parcel as before. Partition convective cloud water and precipitation and detrain convective cloud water in the overshooting layers. + do k = 2, km1 + do i = 1, im + if (cnvflg(i)) then + if(k >= ktcon(i) .and. k < ktcon1(i)) then + dz = zi(i,k) - zi(i,k-1) + gamma = el2orc * qeso(i,k) / (to(i,k)**2) + qrch = qeso(i,k) & + & + gamma * dbyo(i,k) / (hvap * (1. + gamma)) +!j + tem = 0.5 * (xlamue(i,k)+xlamue(i,k-1)) * dz + tem1 = 0.5 * xlamud(i) * dz + factor = 1. + tem - tem1 + qcko(i,k) = ((1.-tem1)*qcko(i,k-1)+tem*0.5* & + & (qo(i,k)+qo(i,k-1)))/factor + qrcko(i,k) = qcko(i,k) +!j + dq = eta(i,k) * (qcko(i,k) - qrch) +! +! check if there is excess moisture to release latent heat +! + if(dq > 0.) then + etah = .5 * (eta(i,k) + eta(i,k-1)) + dp = 1000. * del(i,k) + if(ncloud > 0) then + ptem = c0t(i,k) + c1 + qlk = dq / (eta(i,k) + etah * ptem * dz) + dellal(i,k) = etah * c1 * dz * qlk * g / dp + else + qlk = dq / (eta(i,k) + etah * c0t(i,k) * dz) + endif + qcko(i,k) = qlk + qrch + pwo(i,k) = etah * c0t(i,k) * dz * qlk + cnvwt(i,k) = etah * qlk * g / dp + endif + endif + endif + enddo + enddo +! +! compute updraft velocity square(wu2) +!> - Calculate updraft velocity square(wu2) according to Han et al.'s (2017) \cite han_et_al_2017 equation 7. +! +! bb1 = 2. * (1.+bet1*cd1) +! bb2 = 2. / (f1*(1.+gam1)) +! +! bb1 = 3.9 +! bb2 = 0.67 +! +! bb1 = 2.0 +! bb2 = 4.0 +! + bb1 = 4.0 + bb2 = 0.8 +! +! do i = 1, im +! if (cnvflg(i)) then +! k = kbcon1(i) +! tem = po(i,k) / (rd * to(i,k)) +! wucb = -0.01 * dot(i,k) / (tem * g) +! if(wucb > 0.) then +! wu2(i,k) = wucb * wucb +! else +! wu2(i,k) = 0. +! endif +! endif +! enddo + do k = 2, km1 + do i = 1, im + if (cnvflg(i)) then + if(k > kbcon1(i) .and. k < ktcon(i)) then + dz = zi(i,k) - zi(i,k-1) + tem = 0.25 * bb1 * (drag(i,k)+drag(i,k-1)) * dz + tem1 = 0.5 * bb2 * (buo(i,k)+buo(i,k-1)) * dz + ptem = (1. - tem) * wu2(i,k-1) + ptem1 = 1. + tem + wu2(i,k) = (ptem + tem1) / ptem1 + wu2(i,k) = max(wu2(i,k), 0.) + endif + endif + enddo + enddo +! +! compute updraft velocity averaged over the whole cumulus +! +!> - Calculate the mean updraft velocity within the cloud (wc). + do i = 1, im + wc(i) = 0. + sumx(i) = 0. + enddo + do k = 2, km1 + do i = 1, im + if (cnvflg(i)) then + if(k > kbcon1(i) .and. k < ktcon(i)) then + dz = zi(i,k) - zi(i,k-1) + tem = 0.5 * (sqrt(wu2(i,k)) + sqrt(wu2(i,k-1))) + wc(i) = wc(i) + tem * dz + sumx(i) = sumx(i) + dz + endif + endif + enddo + enddo + do i = 1, im + if(cnvflg(i)) then + if(sumx(i) == 0.) then + cnvflg(i)=.false. + else + wc(i) = wc(i) / sumx(i) + endif + val = 1.e-4 + if (wc(i) < val) cnvflg(i)=.false. + endif + enddo +! +! exchange ktcon with ktcon1 +! + do i = 1, im + if(cnvflg(i)) then + kk = ktcon(i) + ktcon(i) = ktcon1(i) + ktcon1(i) = kk + endif + enddo +! +! this section is ready for cloud water +! + if(ncloud > 0) then +! +! compute liquid and vapor separation at cloud top +! +!> - => Separate the total updraft cloud water at cloud top into vapor and condensate. + do i = 1, im + if(cnvflg(i)) then + k = ktcon(i) - 1 + gamma = el2orc * qeso(i,k) / (to(i,k)**2) + qrch = qeso(i,k) & + & + gamma * dbyo(i,k) / (hvap * (1. + gamma)) + dq = qcko(i,k) - qrch +! +! check if there is excess moisture to release latent heat +! + if(dq > 0.) then + qlko_ktcon(i) = dq + qcko(i,k) = qrch + endif + endif + enddo + endif +! +!--- compute precipitation efficiency in terms of windshear +! +!! - Calculate the wind shear and precipitation efficiency according to equation 58 in Fritsch and Chappell (1980) \cite fritsch_and_chappell_1980 : +!! \f[ +!! E = 1.591 - 0.639\frac{\Delta V}{\Delta z} + 0.0953\left(\frac{\Delta V}{\Delta z}\right)^2 - 0.00496\left(\frac{\Delta V}{\Delta z}\right)^3 +!! \f] +!! where \f$\Delta V\f$ is the integrated horizontal shear over the cloud depth, \f$\Delta z\f$, (the ratio is converted to units of \f$10^{-3} s^{-1}\f$). The variable "edt" is \f$1-E\f$ and is constrained to the range \f$[0,0.9]\f$. + do i = 1, im + if(cnvflg(i)) then + vshear(i) = 0. + endif + enddo + do k = 2, km + do i = 1, im + if (cnvflg(i)) then + if(k > kb(i) .and. k <= ktcon(i)) then + shear= sqrt((uo(i,k)-uo(i,k-1)) ** 2 & + & + (vo(i,k)-vo(i,k-1)) ** 2) + vshear(i) = vshear(i) + shear + endif + endif + enddo + enddo + do i = 1, im + if(cnvflg(i)) then + vshear(i) = 1.e3 * vshear(i) / (zi(i,ktcon(i))-zi(i,kb(i))) + e1=1.591-.639*vshear(i) & + & +.0953*(vshear(i)**2)-.00496*(vshear(i)**3) + edt(i)=1.-e1 + val = .9 + edt(i) = min(edt(i),val) + val = .0 + edt(i) = max(edt(i),val) + endif + enddo +! +!--- what would the change be, that a cloud with unit mass +!--- will do to the environment? +! +!> ## Calculate the tendencies of the state variables (per unit cloud base mass flux) and the cloud base mass flux. +!> - Calculate the change in moist static energy, moisture mixing ratio, and horizontal winds per unit cloud base mass flux for all layers below cloud top from equations B.14 and B.15 from Grell (1993) \cite grell_1993, and for the cloud top from B.16 and B.17. + do k = 1, km + do i = 1, im + if(cnvflg(i) .and. k <= kmax(i)) then + dellah(i,k) = 0. + dellaq(i,k) = 0. + dellau(i,k) = 0. + dellav(i,k) = 0. + endif + enddo + enddo + do n = 1, ntr + do k = 1, km + do i = 1, im + if(cnvflg(i) .and. k <= kmax(i)) then + dellae(i,k,n) = 0. + endif + enddo + enddo + enddo +! +!--- changed due to subsidence and entrainment +! + do k = 2, km1 + do i = 1, im + if (cnvflg(i)) then + if(k > kb(i) .and. k < ktcon(i)) then + dp = 1000. * del(i,k) + dz = zi(i,k) - zi(i,k-1) +! + dv1h = heo(i,k) + dv2h = .5 * (heo(i,k) + heo(i,k-1)) + dv3h = heo(i,k-1) + dv1q = qo(i,k) + dv2q = .5 * (qo(i,k) + qo(i,k-1)) + dv3q = qo(i,k-1) +! + tem = 0.5 * (xlamue(i,k)+xlamue(i,k-1)) + tem1 = xlamud(i) +!j + dellah(i,k) = dellah(i,k) + & + & ( eta(i,k)*dv1h - eta(i,k-1)*dv3h & + & - tem*eta(i,k-1)*dv2h*dz & + & + tem1*eta(i,k-1)*.5*(hcko(i,k)+hcko(i,k-1))*dz & + & ) *g/dp +!j + dellaq(i,k) = dellaq(i,k) + & + & ( eta(i,k)*dv1q - eta(i,k-1)*dv3q & + & - tem*eta(i,k-1)*dv2q*dz & + & + tem1*eta(i,k-1)*.5*(qrcko(i,k)+qcko(i,k-1))*dz & + & ) *g/dp +!j + tem1=eta(i,k)*(uo(i,k)-ucko(i,k)) + tem2=eta(i,k-1)*(uo(i,k-1)-ucko(i,k-1)) + dellau(i,k) = dellau(i,k) + (tem1-tem2) * g/dp +!j + tem1=eta(i,k)*(vo(i,k)-vcko(i,k)) + tem2=eta(i,k-1)*(vo(i,k-1)-vcko(i,k-1)) + dellav(i,k) = dellav(i,k) + (tem1-tem2) * g/dp +!j + endif + endif + enddo + enddo + do n = 1, ntr + do k = 2, km1 + do i = 1, im + if (cnvflg(i)) then + if(k > kb(i) .and. k < ktcon(i)) then + dp = 1000. * del(i,k) +!j + tem1=eta(i,k)*(ctro(i,k,n)-ecko(i,k,n)) + tem2=eta(i,k-1)*(ctro(i,k-1,n)-ecko(i,k-1,n)) + dellae(i,k,n) = dellae(i,k,n) + (tem1-tem2) * g/dp +!j + endif + endif + enddo + enddo + enddo +! +!------- cloud top +! + do i = 1, im + if(cnvflg(i)) then + indx = ktcon(i) + dp = 1000. * del(i,indx) + dv1h = heo(i,indx-1) + dellah(i,indx) = eta(i,indx-1) * & + & (hcko(i,indx-1) - dv1h) * g / dp + dv1q = qo(i,indx-1) + dellaq(i,indx) = eta(i,indx-1) * & + & (qcko(i,indx-1) - dv1q) * g / dp + dellau(i,indx) = eta(i,indx-1) * & + & (ucko(i,indx-1) - uo(i,indx-1)) * g / dp + dellav(i,indx) = eta(i,indx-1) * & + & (vcko(i,indx-1) - vo(i,indx-1)) * g / dp +! +! cloud water +! + dellal(i,indx) = eta(i,indx-1) * & + & qlko_ktcon(i) * g / dp + endif + enddo + do n = 1, ntr + do i = 1, im + if(cnvflg(i)) then + indx = ktcon(i) + dp = 1000. * del(i,indx) + dellae(i,indx,n) = eta(i,indx-1) * & + & (ecko(i,indx-1,n) - ctro(i,indx-1,n)) * g / dp + endif + enddo + enddo +! +! compute convective turn-over time +! +!> - Following Bechtold et al. (2008) \cite bechtold_et_al_2008, calculate the convective turnover time using the mean updraft velocity (wc) and the cloud depth. It is also proportional to the grid size (gdx). + do i= 1, im + if(cnvflg(i)) then + tem = zi(i,ktcon1(i)) - zi(i,kbcon1(i)) + dtconv(i) = tem / wc(i) + tfac = 1. + gdx(i) / 75000. + dtconv(i) = tfac * dtconv(i) + dtconv(i) = max(dtconv(i),dtmin) + dtconv(i) = max(dtconv(i),dt2) + dtconv(i) = min(dtconv(i),dtmax) + endif + enddo +! +!> - Calculate advective time scale (tauadv) using a mean cloud layer wind speed. + do i= 1, im + if(cnvflg(i)) then + sumx(i) = 0. + umean(i) = 0. + endif + enddo + do k = 2, km1 + do i = 1, im + if(cnvflg(i)) then + if(k >= kbcon1(i) .and. k < ktcon1(i)) then + dz = zi(i,k) - zi(i,k-1) + tem = sqrt(u1(i,k)*u1(i,k)+v1(i,k)*v1(i,k)) + umean(i) = umean(i) + tem * dz + sumx(i) = sumx(i) + dz + endif + endif + enddo + enddo + do i= 1, im + if(cnvflg(i)) then + umean(i) = umean(i) / sumx(i) + umean(i) = max(umean(i), 1.) + tauadv(i) = gdx(i) / umean(i) + endif + enddo +! +! compute cloud base mass flux as a function of the mean +! updraft velcoity +! +!> - From Han et al.'s (2017) \cite han_et_al_2017 equation 6, calculate cloud base mass flux as a function of the mean updraft velcoity. +!! As discussed in Han et al. (2017) \cite han_et_al_2017 , when dtconv is larger than tauadv, the convective mixing is not fully conducted before the cumulus cloud is advected out of the grid cell. In this case, therefore, the cloud base mass flux is further reduced in proportion to the ratio of tauadv to dtconv. + do i= 1, im + if(cnvflg(i)) then + k = kbcon(i) + rho = po(i,k)*100. / (rd*to(i,k)) + tfac = tauadv(i) / dtconv(i) + tfac = min(tfac, 1.) + xmb(i) = tfac*betaw*rho*wc(i) + endif + enddo +! +!> - For scale-aware parameterization, the updraft fraction (sigmagfm) is first computed as a function of the lateral entrainment rate at cloud base (see Han et al.'s (2017) \cite han_et_al_2017 equation 4 and 5), following the study by Grell and Freitas (2014) \cite grell_and_freitus_2014. + do i = 1, im + if(cnvflg(i)) then + tem = min(max(xlamue(i,kbcon(i)), 2.e-4), 6.e-4) + tem = 0.2 / tem + tem1 = 3.14 * tem * tem + sigmagfm(i) = tem1 / garea(i) + sigmagfm(i) = max(sigmagfm(i), 0.001) + sigmagfm(i) = min(sigmagfm(i), 0.999) + endif + enddo +! +!> - Then, calculate the reduction factor (scaldfunc) of the vertical convective eddy transport of mass flux as a function of updraft fraction from the studies by Arakawa and Wu (2013) \cite arakawa_and_wu_2013 (also see Han et al.'s (2017) \cite han_et_al_2017 equation 1 and 2). The final cloud base mass flux with scale-aware parameterization is obtained from the mass flux when sigmagfm << 1, multiplied by the reduction factor (Han et al.'s (2017) \cite han_et_al_2017 equation 2). + do i = 1, im + if(cnvflg(i)) then + if (gdx(i) < dxcrt) then + scaldfunc(i) = (1.-sigmagfm(i)) * (1.-sigmagfm(i)) + scaldfunc(i) = max(min(scaldfunc(i), 1.0), 0.) + else + scaldfunc(i) = 1.0 + endif + xmb(i) = xmb(i) * scaldfunc(i) + xmb(i) = min(xmb(i),xmbmax(i)) + endif + enddo + +! +! transport aerosols if present +! +! if (do_aerosols) & +! & call samfshalcnv_aerosols(im, ix, km, itc, ntc, ntr, delt, & +!! & xlamde, xlamdd, cnvflg, jmin, kb, kmax, kbcon, ktcon, fscav, +! & cnvflg, kb, kmax, kbcon, ktcon, fscav, & +!! & edto, xlamd, xmb, c0t, eta, etad, zi, xlamue, xlamud, delp, +! & xmb, c0t, eta, zi, xlamue, xlamud, delp, & +! & qtr, qaero) + +!> ## For the "feedback control", calculate updated values of the state variables by multiplying the cloud base mass flux and the tendencies calculated per unit cloud base mass flux from the static control. +!! - Recalculate saturation specific humidity. +! +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +! + do k = 1, km + do i = 1, im + if (cnvflg(i) .and. k <= kmax(i)) then + qeso(i,k) = 0.01 * fpvs(t1(i,k)) ! fpvs is in pa + qeso(i,k) = eps * qeso(i,k) / (pfld(i,k) + epsm1*qeso(i,k)) + val = 1.e-8 + qeso(i,k) = max(qeso(i,k), val ) + endif + enddo + enddo +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +! +!> - Calculate the temperature tendency from the moist static energy and specific humidity tendencies. +!> - Update the temperature, specific humidity, and horiztonal wind state variables by multiplying the cloud base mass flux-normalized tendencies by the cloud base mass flux. +!> - Accumulate column-integrated tendencies. + do i = 1, im + delhbar(i) = 0. + delqbar(i) = 0. + deltbar(i) = 0. + delubar(i) = 0. + delvbar(i) = 0. + qcond(i) = 0. + enddo + do n = 1, ntr + do i = 1, im + delebar(i,n) = 0. + enddo + enddo + do k = 1, km + do i = 1, im + if (cnvflg(i)) then + if(k > kb(i) .and. k <= ktcon(i)) then + dellat = (dellah(i,k) - hvap * dellaq(i,k)) / cp + t1(i,k) = t1(i,k) + dellat * xmb(i) * dt2 + q1(i,k) = q1(i,k) + dellaq(i,k) * xmb(i) * dt2 +! tem = 1./rcs(i) +! u1(i,k) = u1(i,k) + dellau(i,k) * xmb(i) * dt2 * tem +! v1(i,k) = v1(i,k) + dellav(i,k) * xmb(i) * dt2 * tem + u1(i,k) = u1(i,k) + dellau(i,k) * xmb(i) * dt2 + v1(i,k) = v1(i,k) + dellav(i,k) * xmb(i) * dt2 + dp = 1000. * del(i,k) + delhbar(i) = delhbar(i) + dellah(i,k)*xmb(i)*dp/g + delqbar(i) = delqbar(i) + dellaq(i,k)*xmb(i)*dp/g + deltbar(i) = deltbar(i) + dellat*xmb(i)*dp/g + delubar(i) = delubar(i) + dellau(i,k)*xmb(i)*dp/g + delvbar(i) = delvbar(i) + dellav(i,k)*xmb(i)*dp/g + endif + endif + enddo + enddo + do n = 1, ntr + kk = n+2 + do k = 1, km + do i = 1, im + if (cnvflg(i) .and. k <= kmax(i)) then + if(k <= ktcon(i)) then + dp = 1000. * del(i,k) + ctr(i,k,n) = ctr(i,k,n)+dellae(i,k,n)*xmb(i)*dt2 + delebar(i,n)=delebar(i,n)+dellae(i,k,n)*xmb(i)*dp/g + qtr(i,k,kk) = ctr(i,k,n) + endif + endif + enddo + enddo + enddo +! +!> - Recalculate saturation specific humidity using the updated temperature. + do k = 1, km + do i = 1, im + if (cnvflg(i)) then + if(k > kb(i) .and. k <= ktcon(i)) then + qeso(i,k) = 0.01 * fpvs(t1(i,k)) ! fpvs is in pa + qeso(i,k) = eps * qeso(i,k)/(pfld(i,k) + epsm1*qeso(i,k)) + val = 1.e-8 + qeso(i,k) = max(qeso(i,k), val ) + endif + endif + enddo + enddo +! +!> - Add up column-integrated convective precipitation by multiplying the normalized value by the cloud base mass flux. + do i = 1, im + rntot(i) = 0. + delqev(i) = 0. + delq2(i) = 0. + flg(i) = cnvflg(i) + enddo + do k = km, 1, -1 + do i = 1, im + if (cnvflg(i)) then + if(k < ktcon(i) .and. k > kb(i)) then + rntot(i) = rntot(i) + pwo(i,k) * xmb(i) * .001 * dt2 + endif + endif + enddo + enddo +! +! evaporating rain +! +!> - Determine the evaporation of the convective precipitation and update the integrated convective precipitation. +!> - Update state temperature and moisture to account for evaporation of convective precipitation. +!> - Update column-integrated tendencies to account for evaporation of convective precipitation. + do k = km, 1, -1 + do i = 1, im + if (k <= kmax(i)) then + deltv(i) = 0. + delq(i) = 0. + qevap(i) = 0. + if(cnvflg(i)) then + if(k < ktcon(i) .and. k > kb(i)) then + rn(i) = rn(i) + pwo(i,k) * xmb(i) * .001 * dt2 + endif + endif + if(flg(i) .and. k < ktcon(i)) then + evef = edt(i) * evfact + if(islimsk(i) == 1) evef=edt(i) * evfactl +! if(islimsk(i) == 1) evef=.07 +! if(islimsk(i) == 1) evef = 0. + qcond(i) = evef * (q1(i,k) - qeso(i,k)) & + & / (1. + el2orc * qeso(i,k) / t1(i,k)**2) + dp = 1000. * del(i,k) + if(rn(i) > 0. .and. qcond(i) < 0.) then + qevap(i) = -qcond(i) * (1.-exp(-.32*sqrt(dt2*rn(i)))) + qevap(i) = min(qevap(i), rn(i)*1000.*g/dp) + delq2(i) = delqev(i) + .001 * qevap(i) * dp / g + endif + if(rn(i) > 0. .and. qcond(i) < 0. .and. & + & delq2(i) > rntot(i)) then + qevap(i) = 1000.* g * (rntot(i) - delqev(i)) / dp + flg(i) = .false. + endif + if(rn(i) > 0. .and. qevap(i) > 0.) then + tem = .001 * dp / g + tem1 = qevap(i) * tem + if(tem1 > rn(i)) then + qevap(i) = rn(i) / tem + rn(i) = 0. + else + rn(i) = rn(i) - tem1 + endif + q1(i,k) = q1(i,k) + qevap(i) + t1(i,k) = t1(i,k) - elocp * qevap(i) + deltv(i) = - elocp*qevap(i)/dt2 + delq(i) = + qevap(i)/dt2 + delqev(i) = delqev(i) + .001*dp*qevap(i)/g + endif + delqbar(i) = delqbar(i) + delq(i)*dp/g + deltbar(i) = deltbar(i) + deltv(i)*dp/g + endif + endif + enddo + enddo +!j +! do i = 1, im +! if(me == 31 .and. cnvflg(i)) then +! if(cnvflg(i)) then +! print *, ' shallow delhbar, delqbar, deltbar = ', +! & delhbar(i),hvap*delqbar(i),cp*deltbar(i) +! print *, ' shallow delubar, delvbar = ',delubar(i),delvbar(i) +! print *, ' precip =', hvap*rn(i)*1000./dt2 +! print*,'pdif= ',pfld(i,kbcon(i))-pfld(i,ktcon(i)) +! endif +! enddo +! do n = 1, ntr +! do i = 1, im +! if(me == 31 .and. cnvflg(i)) then +! if(cnvflg(i)) then +! print *, ' tracer delebar = ',delebar(i,n) +! endif +! enddo +! enddo +!j + do i = 1, im + if(cnvflg(i)) then + if(rn(i) < 0. .or. .not.flg(i)) rn(i) = 0. + ktop(i) = ktcon(i) + kbot(i) = kbcon(i) + kcnv(i) = 2 + endif + enddo +! +! convective cloud water +! +!> - Calculate shallow convective cloud water. + do k = 1, km + do i = 1, im + if (cnvflg(i)) then + if (k >= kbcon(i) .and. k < ktcon(i)) then + cnvw(i,k) = cnvwt(i,k) * xmb(i) * dt2 + endif + endif + enddo + enddo + +! +! convective cloud cover +! +!> - Calculate convective cloud cover, which is used when pdf-based cloud fraction is used (i.e., pdfcld=.true.). + do k = 1, km + do i = 1, im + if (cnvflg(i)) then + if (k >= kbcon(i) .and. k < ktcon(i)) then + cnvc(i,k) = 0.04 * log(1. + 675. * eta(i,k) * xmb(i)) + cnvc(i,k) = min(cnvc(i,k), 0.2) + cnvc(i,k) = max(cnvc(i,k), 0.0) + endif + endif + enddo + enddo +! +! cloud water +! +!> - Separate detrained cloud water into liquid and ice species as a function of temperature only. + if (ncloud > 0) then +! + do k = 1, km1 + do i = 1, im + if (cnvflg(i)) then +! if (k > kb(i) .and. k <= ktcon(i)) then + if (k >= kbcon(i) .and. k <= ktcon(i)) then + tem = dellal(i,k) * xmb(i) * dt2 + tem1 = max(0.0, min(1.0, (tcr-t1(i,k))*tcrf)) + if (qtr(i,k,2) > -999.0) then + qtr(i,k,1) = qtr(i,k,1) + tem * tem1 ! ice + qtr(i,k,2) = qtr(i,k,2) + tem *(1.0-tem1) ! water + else + qtr(i,k,1) = qtr(i,k,1) + tem + endif + endif + endif + enddo + enddo +! + endif +!> - Store aerosol concentrations if present + if (do_aerosols) then + do n = 1, ntc + kk = n + itc - 1 + do k = 1, km + do i = 1, im + if(cnvflg(i) .and. rn(i) > 0.) then + if (k <= kmax(i)) qtr(i,k,kk) = qaero(i,k,n) + endif + enddo + enddo + enddo + endif +! +! hchuang code change +! +!> - Calculate and retain the updraft mass flux for dust transport by cumulus convection. +! +!> - Calculate the updraft convective mass flux. + do k = 1, km + do i = 1, im + if(cnvflg(i)) then + if(k >= kb(i) .and. k < ktop(i)) then + ud_mf(i,k) = eta(i,k) * xmb(i) * dt2 + endif + endif + enddo + enddo +!> - save the updraft convective mass flux at cloud top. + do i = 1, im + if(cnvflg(i)) then + k = ktop(i)-1 + dt_mf(i,k) = ud_mf(i,k) + endif + enddo +! +! include TKE contribution from shallow convection +! + if (ntk > 0) then +! + do k = 2, km1 + do i = 1, im + if(cnvflg(i)) then + if(k > kb(i) .and. k < ktop(i)) then + tem = 0.5 * (eta(i,k-1) + eta(i,k)) * xmb(i) + tem1 = pfld(i,k) * 100. / (rd * t1(i,k)) + sigmagfm(i) = max(sigmagfm(i), betaw) + ptem = tem / (sigmagfm(i) * tem1) + qtr(i,k,ntk)=qtr(i,k,ntk)+0.5*sigmagfm(i)*ptem*ptem + endif + endif + enddo + enddo +! + endif + !ser cleanup +!! + return + end +end module samfshalconv_benchmark diff --git a/projects2020/group05/tests/fortran/samfshalconv_serialize.template.f90 b/projects2020/group05/tests/fortran/samfshalconv_serialize.template.f90 new file mode 100644 index 00000000..517add49 --- /dev/null +++ b/projects2020/group05/tests/fortran/samfshalconv_serialize.template.f90 @@ -0,0 +1,1924 @@ +program samfshalconv_serialize + real(kind=8),parameter:: grav =9.80665e+0_8 + real(kind=8),parameter:: cp =1.0046e+3_8 + real(kind=8),parameter:: hvap =2.5000e+6_8 + real(kind=8),parameter:: rv =4.6150e+2_8 + real(kind=8),parameter:: rd =2.8705e+2_8 + real(kind=8),parameter:: fv =rv/rd-1. + real(kind=8),parameter:: t0c =2.7315e+2_8 + real(kind=8),parameter:: cvap =1.8460e+3_8 + real(kind=8),parameter:: cliq =4.1855e+3_8 + real(kind=8),parameter:: eps =rd/rv + real(kind=8),parameter:: epsm1 =rd/rv-1. + + real(kind=8),parameter:: ttp =2.7316e+2_8 + real(kind=8),parameter:: csol =2.1060e+3_8 + real(kind=8),parameter:: hfus =3.3358e+5_8 + real(kind=8),parameter:: psat =6.1078e+2_8 + + integer :: im, ix, km, itc, ntc, ntk, ntr, ncloud + integer, dimension(:), allocatable :: islimsk, kcnv, kbot, ktop, kcnv_ref, kbot_ref, ktop_ref + + real (kind=8) :: clam, c0s, c1, asolfac, pgcon, delt + real (kind=8), dimension(:), allocatable :: psp, garea, hpbl, fscav, rn, rn_ref + real (kind=8), dimension(:, :), allocatable :: delp, prslp, dot, phil, & + q1, t1, u1, v1, cnvw, cnvc, ud_mf, dt_mf, q1_ref, t1_ref, u1_ref, v1_ref, & + cnvw_ref, cnvc_ref, ud_mf_ref, dt_mf_ref + real (kind=8), dimension(:, :, :), allocatable :: qtr, qtr_ref + !character(:), allocatable :: datapath + + !datapath = "/data" + + !$ser init directory='DATAPATH' prefix='Serialized' prefix_ref='Generator' mpi_rank=0 + + !$ser mode read + !$ser savepoint QUOTATION samfshalcnv-in-000000 QUOTATION + !$ser data im=im ix=ix km=km itc=itc ntc=ntc ntk=ntk ntr=ntr ncloud=ncloud + + allocate(psp(im), delp(ix,km), prslp(ix,km), garea(im), hpbl(im), dot(ix,km), & + phil(ix,km), fscav(ntc+1), islimsk(im), kcnv(im), kbot(im), ktop(im), qtr(ix,km,ntr+5), & + q1(ix,km), t1(ix,km), u1(ix,km), v1(ix,km), rn(im), cnvw(ix,km), cnvc(ix,km), & + ud_mf(im,km), dt_mf(im,km), kcnv_ref(im), kbot_ref(im), ktop_ref(im), & + qtr_ref(ix,km,ntr+5), q1_ref(ix,km), t1_ref(ix,km), u1_ref(ix,km), v1_ref(ix,km), & + rn_ref(im), cnvw_ref(ix,km), cnvc_ref(ix,km), ud_mf_ref(im,km), dt_mf_ref(im,km)) + + ! input + !$ser data clam=clam c0s=c0s c1=c1 asolfac=asolfac pgcon=pgcon delt=delt + !$ser data islimsk=islimsk psp=psp delp=delp prslp=prslp garea=garea hpbl=hpbl + !$ser data dot=dot phil=phil fscav=fscav + ! inout + !$ser data kcnv=kcnv kbot=kbot ktop=ktop qtr=qtr q1=q1 t1=t1 u1=u1 v1=v1 rn=rn + !$ser data cnvw=cnvw cnvc=cnvc ud_mf=ud_mf dt_mf=dt_mf + + call samfshalcnv(im, ix, km, delt, itc, ntc, ntk, ntr, delp, & + prslp, psp, phil, qtr, q1, t1, u1, v1, fscav, & + rn, kbot, ktop, kcnv, islimsk, garea, & + dot, ncloud, hpbl, ud_mf, dt_mf, cnvw, cnvc, & + clam, c0s, c1, pgcon, asolfac) + + deallocate(psp, delp, prslp, garea, hpbl, dot, & + phil, fscav, islimsk, kcnv, kbot, ktop, qtr, & + q1, t1, u1, v1, rn, cnvw, cnvc, & + ud_mf, dt_mf, kcnv_ref, kbot_ref, ktop_ref, & + qtr_ref, q1_ref, t1_ref, u1_ref, v1_ref, & + rn_ref, cnvw_ref, cnvc_ref, ud_mf_ref, dt_mf_ref) + + + !$ser cleanup + + write(*,*) 'FINISHED!' + + contains + elemental function fpvs(t) + implicit none + real(kind=8) fpvs + real(kind=8),intent(in):: t + real(kind=8),parameter:: tliq=ttp + real(kind=8),parameter:: tice=ttp-20.0 + real(kind=8),parameter:: dldtl=cvap-cliq + real(kind=8),parameter:: heatl=hvap + real(kind=8),parameter:: xponal=-dldtl/rv + real(kind=8),parameter:: xponbl=-dldtl/rv+heatl/(rv*ttp) + real(kind=8),parameter:: dldti=cvap-csol + real(kind=8),parameter:: heati=hvap+hfus + real(kind=8),parameter:: xponai=-dldti/rv + real(kind=8),parameter:: xponbi=-dldti/rv+heati/(rv*ttp) + real(kind=8) tr,w,pvl,pvi +! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + tr=ttp/t + if(t.ge.tliq) then + fpvs=psat*(tr**xponal)*exp(xponbl*(1.-tr)) + elseif(t.lt.tice) then + fpvs=psat*(tr**xponai)*exp(xponbi*(1.-tr)) + else + w=(t-tice)/(tliq-tice) + pvl=psat*(tr**xponal)*exp(xponbl*(1.-tr)) + pvi=psat*(tr**xponai)*exp(xponbi*(1.-tr)) + fpvs=w*pvl+(1.-w)*pvi + endif +! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + end function + subroutine samfshalcnv(im,ix,km,delt,itc,ntc,ntk,ntr,delp, & + & prslp,psp,phil,qtr,q1,t1,u1,v1,fscav, & + & rn,kbot,ktop,kcnv,islimsk,garea, & + & dot,ncloud,hpbl,ud_mf,dt_mf,cnvw,cnvc, & + & clam,c0s,c1,pgcon,asolfac) +! + implicit none +! + integer, intent(in) :: im, ix, km, itc, ntc, ntk, ntr, ncloud + integer, intent(in) :: islimsk(im) + real(kind=8), intent(in) :: delt + real(kind=8), intent(in) :: psp(im), delp(ix,km), & + & prslp(ix,km), garea(im), hpbl(im), dot(ix,km), phil(ix,km) +! + real(kind=8), intent(in) :: fscav(ntc) + integer, intent(inout) :: kcnv(im) + real(kind=8), intent(inout) :: qtr(ix,km,ntr+2), & + & q1(ix,km), t1(ix,km), u1(ix,km), v1(ix,km) +! + integer, intent(out) :: kbot(im), ktop(im) + real(kind=8), intent(out) :: rn(im), & + & cnvw(ix,km), cnvc(ix,km), ud_mf(im,km), dt_mf(im,km) +! + real(kind=8), intent(in) :: clam, c0s, c1, & + & asolfac, pgcon +! +! local variables + integer i,j,indx, k, kk, km1, n + integer kpbl(im) +! + real(kind=8) clamd, tkemx, tkemn, dtke +! + real(kind=8) dellat, delta, & + & c0l, d0, & + & desdt, dp, & + & dq, dqsdp, dqsdt, dt, & + & dt2, dtmax, dtmin, dxcrt, & + & dv1h, dv2h, dv3h, & + & dv1q, dv2q, dv3q, & + & dz, dz1, e1, & + & el2orc, elocp, aafac, cm, & + & es, etah, h1, & + & evef, evfact, evfactl, fact1, & + & fact2, factor, dthk, & + & g, gamma, pprime, betaw, & + & qlk, qrch, qs, & + & rfact, shear, tfac, & + & val, val1, val2, & + & w1, w1l, w1s, w2, & + & w2l, w2s, w3, w3l, & + & w3s, w4, w4l, w4s, & + & rho, tem, tem1, tem2, & + & ptem, ptem1 +! + integer kb(im), kbcon(im), kbcon1(im), & + & ktcon(im), ktcon1(im), ktconn(im), & + & kbm(im), kmax(im) +! + real(kind=8) aa1(im), cina(im), & + & tkemean(im), clamt(im), & + & ps(im), del(ix,km), prsl(ix,km), & + & umean(im), tauadv(im), gdx(im), & + & delhbar(im), delq(im), delq2(im), & + & delqbar(im), delqev(im), deltbar(im), & + & deltv(im), dtconv(im), edt(im), & + & pdot(im), po(im,km), & + & qcond(im), qevap(im), hmax(im), & + & rntot(im), vshear(im), & + & xlamud(im), xmb(im), xmbmax(im), & + & delebar(im,ntr), & + & delubar(im), delvbar(im) +! + real(kind=8) c0(im) +! + real(kind=8) crtlamd +! + real(kind=8) cinpcr, cinpcrmx, cinpcrmn, & + & cinacr, cinacrmx, cinacrmn +! +! parameters for updraft velocity calculation + real(kind=8) bet1, cd1, f1, gam1, & + & bb1, bb2 +! & bb1, bb2, wucb +!c +! physical parameters +! parameter(g=grav,asolfac=0.89) + parameter(g=grav) + parameter(elocp=hvap/cp, & + & el2orc=hvap*hvap/(rv*cp)) +! parameter(c0s=0.002,c1=5.e-4,d0=.01) +! parameter(d0=.01) + parameter(d0=.001) +! parameter(c0l=c0s*asolfac) +! +! asolfac: aerosol-aware parameter based on Lim & Hong (2012) +! asolfac= cx / c0s(=.002) +! cx = min([-0.7 ln(Nccn) + 24]*1.e-4, c0s) +! Nccn: CCN number concentration in cm^(-3) +! Until a realistic Nccn is provided, Nccns are assumed +! as Nccn=100 for sea and Nccn=1000 for land +! + parameter(cm=1.0,delta=fv) + parameter(fact1=(cvap-cliq)/rv,fact2=hvap/rv-fact1*t0c) + parameter(clamd=0.1,tkemx=0.65,tkemn=0.05) + parameter(dtke=tkemx-tkemn) + parameter(dthk=25.) + parameter(cinpcrmx=180.,cinpcrmn=120.) +! parameter(cinacrmx=-120.,cinacrmn=-120.) + parameter(cinacrmx=-120.,cinacrmn=-80.) + parameter(crtlamd=3.e-4) + parameter(dtmax=10800.,dtmin=600.) + parameter(bet1=1.875,cd1=.506,f1=2.0,gam1=.5) + parameter(betaw=.03,dxcrt=15.e3) + parameter(h1=0.33333333) +! local variables and arrays + real(kind=8) pfld(im,km), to(im,km), qo(im,km), & + & uo(im,km), vo(im,km), qeso(im,km), & + & ctr(im,km,ntr), ctro(im,km,ntr) +! for aerosol transport + real(kind=8) qaero(im,km,ntc) +! for updraft velocity calculation + real(kind=8) wu2(im,km), buo(im,km), drag(im,km) + real(kind=8) wc(im), scaldfunc(im), sigmagfm(im) +! +! cloud water +! real(kind=8) qlko_ktcon(im), dellal(im,km), tvo(im,km), + real(kind=8) qlko_ktcon(im), dellal(im,km), & + & dbyo(im,km), zo(im,km), xlamue(im,km), & + & heo(im,km), heso(im,km), & + & dellah(im,km), dellaq(im,km), & + & dellae(im,km,ntr), & + & dellau(im,km), dellav(im,km), hcko(im,km), & + & ucko(im,km), vcko(im,km), qcko(im,km), & + & qrcko(im,km), ecko(im,km,ntr), & + & eta(im,km), & + & zi(im,km), pwo(im,km), c0t(im,km), & + & sumx(im), tx1(im), cnvwt(im,km) +! + logical do_aerosols, totflg, cnvflg(im), flg(im) +! + real(kind=8) tf, tcr, tcrf + parameter (tf=233.16, tcr=263.16, tcrf=1.0/(tcr-tf)) + +!------------------SERIALIZATION--------------------------------------- + !ser init directory='/data_out' prefix='Serialized' prefix_ref='SavedData' +! +!----------------------------------------------------------------------- +!> ## Determine whether to perform aerosol transport + do_aerosols = (itc > 0) .and. (ntc > 0) .and. (ntr > 0) + if (do_aerosols) do_aerosols = (ntr >= itc + ntc - 3) +! +!************************************************************************ +! convert input Pa terms to Cb terms -- Moorthi +!> ## Compute preliminary quantities needed for the static and feedback control portions of the algorithm. +!> - Convert input pressure terms to centibar units. + ps = psp * 0.001 + prsl = prslp * 0.001 + del = delp * 0.001 +!************************************************************************ +! + km1 = km - 1 +! +! initialize arrays +! +!> - Initialize column-integrated and other single-value-per-column variable arrays. + do i=1,im + cnvflg(i) = .true. + if(kcnv(i) == 1) cnvflg(i) = .false. + if(cnvflg(i)) then + kbot(i)=km+1 + ktop(i)=0 + endif + rn(i)=0. + kbcon(i)=km + ktcon(i)=1 + ktconn(i)=1 + kb(i)=km + pdot(i) = 0. + qlko_ktcon(i) = 0. + edt(i) = 0. + aa1(i) = 0. + cina(i) = 0. + vshear(i) = 0. + gdx(i) = sqrt(garea(i)) + enddo +!! +!> - Return to the calling routine if deep convection is present or the surface buoyancy flux is negative. + totflg = .true. + do i=1,im + totflg = totflg .and. (.not. cnvflg(i)) + enddo + if(totflg) return +!! +!> - determine aerosol-aware rain conversion parameter over land + do i=1,im + if(islimsk(i) == 1) then + c0(i) = c0s*asolfac + else + c0(i) = c0s + endif + enddo +! +!> - determine rain conversion parameter above the freezing level which exponentially decreases with decreasing temperature from Han et al.'s (2017) \cite han_et_al_2017 equation 8. + do k = 1, km + do i = 1, im + if(t1(i,k) > 273.16) then + c0t(i,k) = c0(i) + else + tem = d0 * (t1(i,k) - 273.16) + tem1 = exp(tem) + c0t(i,k) = c0(i) * tem1 + endif + enddo + enddo +! +!> - Initialize convective cloud water and cloud cover to zero. + do k = 1, km + do i = 1, im + cnvw(i,k) = 0. + cnvc(i,k) = 0. + enddo + enddo +! hchuang code change +!> - Initialize updraft mass fluxes to zero. + do k = 1, km + do i = 1, im + ud_mf(i,k) = 0. + dt_mf(i,k) = 0. + enddo + enddo +! + dt2 = delt +! +! model tunable parameters are all here +! clam = .3 +! aafac = .1 + aafac = .05 +! evef = 0.07 + evfact = 0.3 + evfactl = 0.3 +! +! pgcon = 0.7 ! Gregory et al. (1997, QJRMS) +! pgcon = 0.55 ! Zhang & Wu (2003,JAS) + w1l = -8.e-3 + w2l = -4.e-2 + w3l = -5.e-3 + w4l = -5.e-4 + w1s = -2.e-4 + w2s = -2.e-3 + w3s = -1.e-3 + w4s = -2.e-5 +! +! define top layer for search of the downdraft originating layer +! and the maximum thetae for updraft +! +!> - Determine maximum indices for the parcel starting point (kbm) and cloud top (kmax). + do i=1,im + kbm(i) = km + kmax(i) = km + tx1(i) = 1.0 / ps(i) + enddo +! + do k = 1, km + do i=1,im + if (prsl(i,k)*tx1(i) > 0.70) kbm(i) = k + 1 + if (prsl(i,k)*tx1(i) > 0.60) kmax(i) = k + 1 + enddo + enddo + do i=1,im + kbm(i) = min(kbm(i),kmax(i)) + enddo +! +! hydrostatic height assume zero terr and compute +! updraft entrainment rate as an inverse function of height +! +!> - Calculate hydrostatic height at layer centers assuming a flat surface (no terrain) from the geopotential. + do k = 1, km + do i=1,im + zo(i,k) = phil(i,k) / g + enddo + enddo +!> - Calculate interface height + do k = 1, km1 + do i=1,im + zi(i,k) = 0.5*(zo(i,k)+zo(i,k+1)) + enddo + enddo +! +! pbl height +! +!> - Find the index for the PBL top using the PBL height; enforce that it is lower than the maximum parcel starting level. + do i=1,im + flg(i) = cnvflg(i) + kpbl(i)= 1 + enddo + do k = 2, km1 + do i=1,im + if (flg(i) .and. zo(i,k) <= hpbl(i)) then + kpbl(i) = k + else + flg(i) = .false. + endif + enddo + enddo + do i=1,im + kpbl(i)= min(kpbl(i),kbm(i)) + enddo +! +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +! convert surface pressure to mb from cb +! +!> - Convert prsl from centibar to millibar, set normalized mass flux to 1, cloud properties to 0, and save model state variables (after advection/turbulence). + do k = 1, km + do i = 1, im + if (cnvflg(i) .and. k <= kmax(i)) then + pfld(i,k) = prsl(i,k) * 10.0 + eta(i,k) = 1. + hcko(i,k) = 0. + qcko(i,k) = 0. + qrcko(i,k)= 0. + ucko(i,k) = 0. + vcko(i,k) = 0. + dbyo(i,k) = 0. + pwo(i,k) = 0. + dellal(i,k) = 0. + to(i,k) = t1(i,k) + qo(i,k) = q1(i,k) + uo(i,k) = u1(i,k) + vo(i,k) = v1(i,k) +! uo(i,k) = u1(i,k) * rcs(i) +! vo(i,k) = v1(i,k) * rcs(i) + wu2(i,k) = 0. + buo(i,k) = 0. + drag(i,k) = 0. + cnvwt(i,k) = 0. + endif + enddo + enddo +! +! initialize tracer variables +! + do n = 3, ntr+2 + kk = n-2 + do k = 1, km + do i = 1, im + if (cnvflg(i) .and. k <= kmax(i)) then + ctr(i,k,kk) = qtr(i,k,n) + ctro(i,k,kk) = qtr(i,k,n) + ecko(i,k,kk) = 0. + endif + enddo + enddo + enddo +!> - Calculate saturation specific humidity and enforce minimum moisture values. + do k = 1, km + do i=1,im + if (cnvflg(i) .and. k <= kmax(i)) then + qeso(i,k) = 0.01 * fpvs(to(i,k)) ! fpvs is in pa + qeso(i,k) = eps * qeso(i,k) / (pfld(i,k) + epsm1*qeso(i,k)) + val1 = 1.e-8 + qeso(i,k) = max(qeso(i,k), val1) + val2 = 1.e-10 + qo(i,k) = max(qo(i,k), val2 ) +! qo(i,k) = min(qo(i,k),qeso(i,k)) +! tvo(i,k) = to(i,k) + delta * to(i,k) * qo(i,k) + endif + enddo + enddo +! +! compute moist static energy +! +!> - Calculate moist static energy (heo) and saturation moist static energy (heso). + do k = 1, km + do i=1,im + if (cnvflg(i) .and. k <= kmax(i)) then +! tem = g * zo(i,k) + cp * to(i,k) + tem = phil(i,k) + cp * to(i,k) + heo(i,k) = tem + hvap * qo(i,k) + heso(i,k) = tem + hvap * qeso(i,k) +! heo(i,k) = min(heo(i,k),heso(i,k)) + endif + enddo + enddo + + !$ser mode write + !$ser savepoint QUOTATION samfshalcnv-part2-input QUOTATION + !$ser data ix=ix im=im km=km + !$ser data cnvwt=cnvwt dellal=dellal ktconn=ktconn pwo=pwo !output + !$ser data qlko_ktcon=qlko_ktcon qrcko=qrcko xmbmax=xmbmax !output + !$ser data islimsk=islimsk dot=dot qtr=qtr kpbl=kpbl kb=kb + !$ser data kbcon=kbcon kbcon1=kbcon1 ktcon=ktcon ktcon1=ktcon1 + !$ser data kbm=kbm kmax=kmax aa1=aa1 cina=cina tkemean=tkemean + !$ser data clamt=clamt del=del edt=edt pdot=pdot po=po hmax=hmax + !$ser data vshear=vshear xlamud=xlamud pfld=pfld + !$ser data to=to qo=qo uo=uo vo=vo qeso=qeso ctro=ctro wu2=wu2 + !$ser data buo=buo drag=drag wc=wc dbyo=dbyo zo=zo xlamue=xlamue + !$ser data heo=heo heso=heso hcko=hcko ucko=ucko vcko=vcko qcko=qcko ecko=ecko + !$ser data eta=eta zi=zi c0t=c0t sumx=sumx cnvflg=cnvflg flg=flg +! +! determine level with largest moist static energy within pbl +! this is the level where updraft starts +! +!> ## Perform calculations related to the updraft of the entraining/detraining cloud model ("static control"). +!> - Search in the PBL for the level of maximum moist static energy to start the ascending parcel. + do i=1,im + if (cnvflg(i)) then + hmax(i) = heo(i,1) + kb(i) = 1 + endif + enddo + do k = 2, km + do i=1,im + if (cnvflg(i) .and. k <= kpbl(i)) then + if(heo(i,k) > hmax(i)) then + kb(i) = k + hmax(i) = heo(i,k) + endif + endif + enddo + enddo + +! +!> - Calculate the temperature, water vapor mixing ratio, and pressure at interface levels. + do k = 1, km1 + do i=1,im + if (cnvflg(i) .and. k <= kmax(i)-1) then + dz = .5 * (zo(i,k+1) - zo(i,k)) + dp = .5 * (pfld(i,k+1) - pfld(i,k)) + es = 0.01 * fpvs(to(i,k+1)) ! fpvs is in pa + pprime = pfld(i,k+1) + epsm1 * es + qs = eps * es / pprime + dqsdp = - qs / pprime + desdt = es * (fact1 / to(i,k+1) + fact2 / (to(i,k+1)**2)) + dqsdt = qs * pfld(i,k+1) * desdt / (es * pprime) + gamma = el2orc * qeso(i,k+1) / (to(i,k+1)**2) + dt = (g * dz + hvap * dqsdp * dp) / (cp * (1. + gamma)) + dq = dqsdt * dt + dqsdp * dp + to(i,k) = to(i,k+1) + dt + qo(i,k) = qo(i,k+1) + dq + po(i,k) = .5 * (pfld(i,k) + pfld(i,k+1)) + endif + enddo + enddo +! +!> - Recalculate saturation specific humidity, moist static energy, saturation moist static energy, and horizontal momentum on interface levels. Enforce minimum specific humidity. + do k = 1, km1 + do i=1,im + if (cnvflg(i) .and. k <= kmax(i)-1) then + qeso(i,k) = 0.01 * fpvs(to(i,k)) ! fpvs is in pa + qeso(i,k) = eps * qeso(i,k) / (po(i,k) + epsm1*qeso(i,k)) + val1 = 1.e-8 + qeso(i,k) = max(qeso(i,k), val1) + val2 = 1.e-10 + qo(i,k) = max(qo(i,k), val2 ) +! qo(i,k) = min(qo(i,k),qeso(i,k)) + heo(i,k) = .5 * g * (zo(i,k) + zo(i,k+1)) + & + & cp * to(i,k) + hvap * qo(i,k) + heso(i,k) = .5 * g * (zo(i,k) + zo(i,k+1)) + & + & cp * to(i,k) + hvap * qeso(i,k) + uo(i,k) = .5 * (uo(i,k) + uo(i,k+1)) + vo(i,k) = .5 * (vo(i,k) + vo(i,k+1)) + endif + enddo + enddo + do n = 1, ntr + do k = 1, km1 + do i=1,im + if (cnvflg(i) .and. k <= kmax(i)-1) then + ctro(i,k,n) = .5 * (ctro(i,k,n) + ctro(i,k+1,n)) + endif + enddo + enddo + enddo + + !$ser mode write + !$ser savepoint QUOTATION samfshalcnv-part2-1 QUOTATION + !$ser data ix=ix km=km + !$ser data hmax=hmax heo=heo heso=heso kb=kb + !$ser data to=to qeso=qeso po=po qo=qo uo=uo vo=vo +! +! look for the level of free convection as cloud base +! +!> - Search below the index "kbm" for the level of free convection (LFC) where the condition \f$h_b > h^*\f$ is first met, where \f$h_b, h^*\f$ are the state moist static energy at the parcel's starting level and saturation moist static energy, respectively. Set "kbcon" to the index of the LFC. + do i=1,im + flg(i) = cnvflg(i) + if(flg(i)) kbcon(i) = kmax(i) + enddo + do k = 2, km1 + do i=1,im + if (flg(i) .and. k < kbm(i)) then + if(k > kb(i) .and. heo(i,kb(i)) > heso(i,k)) then + kbcon(i) = k + flg(i) = .false. + endif + endif + enddo + enddo +! + do i=1,im + if(cnvflg(i)) then + if(kbcon(i) == kmax(i)) cnvflg(i) = .false. + endif + enddo +!! +!> - If no LFC, return to the calling routine without modifying state variables. + totflg = .true. + do i=1,im + totflg = totflg .and. (.not. cnvflg(i)) + enddo + if(totflg) return +!! +!> - Determine the vertical pressure velocity at the LFC. After Han and Pan (2011) \cite han_and_pan_2011 , determine the maximum pressure thickness between a parcel's starting level and the LFC. If a parcel doesn't reach the LFC within the critical thickness, then the convective inhibition is deemed too great for convection to be triggered, and the subroutine returns to the calling routine without modifying the state variables. + do i=1,im + if(cnvflg(i)) then +! pdot(i) = 10.* dot(i,kbcon(i)) + pdot(i) = 0.01 * dot(i,kbcon(i)) ! Now dot is in Pa/s + endif + enddo +! +! turn off convection if pressure depth between parcel source level +! and cloud base is larger than a critical value, cinpcr +! + do i=1,im + if(cnvflg(i)) then + if(islimsk(i) == 1) then + w1 = w1l + w2 = w2l + w3 = w3l + w4 = w4l + else + w1 = w1s + w2 = w2s + w3 = w3s + w4 = w4s + endif + if(pdot(i) <= w4) then + tem = (pdot(i) - w4) / (w3 - w4) + elseif(pdot(i) >= -w4) then + tem = - (pdot(i) + w4) / (w4 - w3) + else + tem = 0. + endif + val1 = -1. + tem = max(tem,val1) + val2 = 1. + tem = min(tem,val2) + ptem = 1. - tem + ptem1= .5*(cinpcrmx-cinpcrmn) + cinpcr = cinpcrmx - ptem * ptem1 + tem1 = pfld(i,kb(i)) - pfld(i,kbcon(i)) + if(tem1 > cinpcr) then + cnvflg(i) = .false. + endif + endif + enddo + + !$ser mode write + !$ser savepoint QUOTATION samfshalcnv-part2-2 QUOTATION + !$ser data ix=ix km=km + !$ser data cnvflg=cnvflg pdot=pdot + +!! + totflg = .true. + do i=1,im + totflg = totflg .and. (.not. cnvflg(i)) + enddo + if(totflg) return +! +! turbulent entrainment rate assumed to be proportional +! to subcloud mean TKE +! + if(ntk > 0) then +! + do i= 1, im + if(cnvflg(i)) then + sumx(i) = 0. + tkemean(i) = 0. + endif + enddo + do k = 1, km1 + do i = 1, im + if(cnvflg(i)) then + if(k >= kb(i) .and. k < kbcon(i)) then + dz = zo(i,k+1) - zo(i,k) + tem = 0.5 * (qtr(i,k,ntk)+qtr(i,k+1,ntk)) + tkemean(i) = tkemean(i) + tem * dz + sumx(i) = sumx(i) + dz + endif + endif + enddo + enddo +! + do i= 1, im + if(cnvflg(i)) then + tkemean(i) = tkemean(i) / sumx(i) + if(tkemean(i) > tkemx) then + clamt(i) = clam + clamd + else if(tkemean(i) < tkemn) then + clamt(i) = clam - clamd + else + tem = tkemx - tkemean(i) + tem1 = 1. - 2. * tem / dtke + clamt(i) = clam + clamd * tem1 + endif + endif + enddo +! + else +! + do i= 1, im + if(cnvflg(i)) then + clamt(i) = clam + endif + enddo +! + endif +!! +! +! assume updraft entrainment rate +! is an inverse function of height +! + do k = 1, km1 + do i=1,im + if(cnvflg(i)) then + xlamue(i,k) = clamt(i) / zi(i,k) + endif + enddo + enddo + do i=1,im + if(cnvflg(i)) then + xlamue(i,km) = xlamue(i,km1) + endif + enddo +! +! specify the detrainment rate for the updrafts +! +!! (The updraft detrainment rate is set constant and equal to the entrainment rate at cloud base.) +!! +!> - The updraft detrainment rate is vertically constant and proportional to clamt + do i = 1, im + if(cnvflg(i)) then +! xlamud(i) = xlamue(i,kbcon(i)) +! xlamud(i) = crtlamd + xlamud(i) = 0.001 * clamt(i) + endif + enddo +! +! determine updraft mass flux for the subcloud layers +! +!> - Calculate the normalized mass flux for subcloud and in-cloud layers according to Pan and Wu (1995) \cite pan_and_wu_1995 equation 1: +!! \f[ +!! \frac{1}{\eta}\frac{\partial \eta}{\partial z} = \lambda_e - \lambda_d +!! \f] +!! where \f$\eta\f$ is the normalized mass flux, \f$\lambda_e\f$ is the entrainment rate and \f$\lambda_d\f$ is the detrainment rate. The normalized mass flux increases upward below the cloud base and decreases upward above. + do k = km1, 1, -1 + do i = 1, im + if (cnvflg(i)) then + if(k < kbcon(i) .and. k >= kb(i)) then + dz = zi(i,k+1) - zi(i,k) + ptem = 0.5*(xlamue(i,k)+xlamue(i,k+1))-xlamud(i) + eta(i,k) = eta(i,k+1) / (1. + ptem * dz) + endif + endif + enddo + enddo +! +! compute mass flux above cloud base +! + do i = 1, im + flg(i) = cnvflg(i) + enddo + do k = 2, km1 + do i = 1, im + if(flg(i))then + if(k > kbcon(i) .and. k < kmax(i)) then + dz = zi(i,k) - zi(i,k-1) + ptem = 0.5*(xlamue(i,k)+xlamue(i,k-1))-xlamud(i) + eta(i,k) = eta(i,k-1) * (1 + ptem * dz) + if(eta(i,k) <= 0.) then + kmax(i) = k + ktconn(i) = k + kbm(i) = min(kbm(i),kmax(i)) + flg(i) = .false. + endif + endif + endif + enddo + enddo +! +! compute updraft cloud property +! +!> - Set cloud properties equal to the state variables at updraft starting level (kb). + do i = 1, im + if(cnvflg(i)) then + indx = kb(i) + hcko(i,indx) = heo(i,indx) + ucko(i,indx) = uo(i,indx) + vcko(i,indx) = vo(i,indx) + endif + enddo +! for tracers + do n = 1, ntr + do i = 1, im + if(cnvflg(i)) then + indx = kb(i) + ecko(i,indx,n) = ctro(i,indx,n) + endif + enddo + enddo + + !$ser mode write + !$ser savepoint QUOTATION samfshalcnv-part2-3 QUOTATION + !$ser data ix=ix km=km + !$ser data clamt=clamt xlamud=xlamud xlamue=xlamue eta=eta + !$ser data kmax=kmax kbm=kbm hcko=hcko ucko=ucko vcko=vcko + !$ser data tkemean=tkemean sumx=sumx + !$ser data cnvflg=cnvflg kb=kb zi=zi heo=heo dbyo=dbyo + !$ser data heso=heso pgcon=pgcon uo=uo vo=vo +! +! cm is an enhancement factor in entrainment rates for momentum +! +!> - Calculate the cloud properties as a parcel ascends, modified by entrainment and detrainment. Discretization follows Appendix B of Grell (1993) \cite grell_1993 . Following Han and Pan (2006) \cite han_and_pan_2006, the convective momentum transport is reduced by the convection-induced pressure gradient force by the constant "pgcon", currently set to 0.55 after Zhang and Wu (2003) \cite zhang_and_wu_2003 . + do k = 2, km1 + do i = 1, im + if (cnvflg(i)) then + if(k > kb(i) .and. k < kmax(i)) then + dz = zi(i,k) - zi(i,k-1) + tem = 0.5 * (xlamue(i,k)+xlamue(i,k-1)) * dz + tem1 = 0.5 * xlamud(i) * dz + factor = 1. + tem - tem1 + hcko(i,k) = ((1.-tem1)*hcko(i,k-1)+tem*0.5* & + & (heo(i,k)+heo(i,k-1)))/factor + dbyo(i,k) = hcko(i,k) - heso(i,k) +! + tem = 0.5 * cm * tem + factor = 1. + tem + ptem = tem + pgcon + ptem1= tem - pgcon + ucko(i,k) = ((1.-tem)*ucko(i,k-1)+ptem*uo(i,k) & + & +ptem1*uo(i,k-1))/factor + vcko(i,k) = ((1.-tem)*vcko(i,k-1)+ptem*vo(i,k) & + & +ptem1*vo(i,k-1))/factor + endif + endif + enddo + enddo + do n = 1, ntr + do k = 2, km1 + do i = 1, im + if (cnvflg(i)) then + if(k > kb(i) .and. k < kmax(i)) then + dz = zi(i,k) - zi(i,k-1) + tem = 0.25 * (xlamue(i,k)+xlamue(i,k-1)) * dz + factor = 1. + tem + ecko(i,k,n) = ((1.-tem)*ecko(i,k-1,n)+tem* & + & (ctro(i,k,n)+ctro(i,k-1,n)))/factor + endif + endif + enddo + enddo + enddo + + !$ser mode write + !$ser savepoint QUOTATION samfshalcnv-part2-4 QUOTATION + !$ser data ix=ix km=km + !$ser data hcko=hcko dbyo=dbyo ucko=ucko vcko=vcko + !$ser data cnvflg=cnvflg kmax=kmax kbm=kbm kbcon=kbcon + !$ser data kbcon1=kbcon1 flg=flg pfld=pfld +! +! taking account into convection inhibition due to existence of +! dry layers below cloud base +! +!> - With entrainment, recalculate the LFC as the first level where buoyancy is positive. The difference in pressure levels between LFCs calculated with/without entrainment must be less than a threshold (currently 25 hPa). Otherwise, convection is inhibited and the scheme returns to the calling routine without modifying the state variables. This is the subcloud dryness trigger modification discussed in Han and Pan (2011) \cite han_and_pan_2011. + do i=1,im + flg(i) = cnvflg(i) + kbcon1(i) = kmax(i) + enddo + do k = 2, km1 + do i=1,im + if (flg(i) .and. k < kbm(i)) then + if(k >= kbcon(i) .and. dbyo(i,k) > 0.) then + kbcon1(i) = k + flg(i) = .false. + endif + endif + enddo + enddo + do i=1,im + if(cnvflg(i)) then + if(kbcon1(i) == kmax(i)) cnvflg(i) = .false. + endif + enddo + do i=1,im + if(cnvflg(i)) then + tem = pfld(i,kbcon(i)) - pfld(i,kbcon1(i)) + if(tem > dthk) then + cnvflg(i) = .false. + endif + endif + enddo + + !$ser mode write + !$ser savepoint QUOTATION samfshalcnv-part2-5 QUOTATION + !$ser data ix=ix km=km + !$ser data cnvflg=cnvflg kbcon1=kbcon1 flg=flg + !$ser data cina=cina kb=kb zo=zo qeso=qeso to=to + !$ser data dbyo=dbyo qo=qo pdot=pdot islimsk=islimsk + +!! + totflg = .true. + do i = 1, im + totflg = totflg .and. (.not. cnvflg(i)) + enddo + if(totflg) return +!! +! +! calculate convective inhibition +! +!> - Calculate additional trigger condition of the convective inhibition (CIN) according to Han et al.'s (2017) \cite han_et_al_2017 equation 13. + do k = 2, km1 + do i = 1, im + if (cnvflg(i)) then + if(k > kb(i) .and. k < kbcon1(i)) then + dz1 = zo(i,k+1) - zo(i,k) + gamma = el2orc * qeso(i,k) / (to(i,k)**2) + rfact = 1. + delta * cp * gamma & + & * to(i,k) / hvap + cina(i) = cina(i) + & +! & dz1 * eta(i,k) * (g / (cp * to(i,k))) + & dz1 * (g / (cp * to(i,k))) & + & * dbyo(i,k) / (1. + gamma) & + & * rfact + val = 0. + cina(i) = cina(i) + & +! & dz1 * eta(i,k) * g * delta * + & dz1 * g * delta * & + & max(val,(qeso(i,k) - qo(i,k))) + endif + endif + enddo + enddo +!> - Turn off convection if the CIN is less than a critical value (cinacr) which is inversely proportional to the large-scale vertical velocity. + do i = 1, im + if(cnvflg(i)) then +! + if(islimsk(i) == 1) then + w1 = w1l + w2 = w2l + w3 = w3l + w4 = w4l + else + w1 = w1s + w2 = w2s + w3 = w3s + w4 = w4s + endif + if(pdot(i) <= w4) then + tem = (pdot(i) - w4) / (w3 - w4) + elseif(pdot(i) >= -w4) then + tem = - (pdot(i) + w4) / (w4 - w3) + else + tem = 0. + endif + + val1 = -1. + tem = max(tem,val1) + val2 = 1. + tem = min(tem,val2) + tem = 1. - tem + tem1= .5*(cinacrmx-cinacrmn) + cinacr = cinacrmx - tem * tem1 +! +! cinacr = cinacrmx + if(cina(i) < cinacr) cnvflg(i) = .false. + endif + enddo + + !$ser mode write + !$ser savepoint QUOTATION samfshalcnv-part2-6 QUOTATION + !$ser data ix=ix km=km + !$ser data cnvflg=cnvflg cina=cina +!! + totflg = .true. + do i=1,im + totflg = totflg .and. (.not. cnvflg(i)) + enddo + if(totflg) return +!! +! +! determine first guess cloud top as the level of zero buoyancy +! limited to the level of P/Ps=0.7 +! +!> - Calculate the cloud top as the first level where parcel buoyancy becomes negative; the maximum possible value is at \f$p=0.7p_{sfc}\f$. + do i = 1, im + flg(i) = cnvflg(i) + if(flg(i)) ktcon(i) = kbm(i) + enddo + do k = 2, km1 + do i=1,im + if (flg(i) .and. k < kbm(i)) then + if(k > kbcon1(i) .and. dbyo(i,k) < 0.) then + ktcon(i) = k + flg(i) = .false. + endif + endif + enddo + enddo +! +! specify upper limit of mass flux at cloud base +! +!> - Calculate the maximum value of the cloud base mass flux using the CFL-criterion-based formula of Han and Pan (2011) \cite han_and_pan_2011, equation 7. + do i = 1, im + if(cnvflg(i)) then +! xmbmax(i) = .1 +! + k = kbcon(i) + dp = 1000. * del(i,k) + xmbmax(i) = dp / (2. * g * dt2) +! +! xmbmax(i) = dp / (g * dt2) +! +! tem = dp / (g * dt2) +! xmbmax(i) = min(tem, xmbmax(i)) + endif + enddo +! +! compute cloud moisture property and precipitation +! +!> - Set cloud moisture property equal to the enviromental moisture at updraft starting level (kb). + do i = 1, im + if (cnvflg(i)) then + aa1(i) = 0. + qcko(i,kb(i)) = qo(i,kb(i)) + qrcko(i,kb(i)) = qo(i,kb(i)) + endif + enddo +!> - Calculate the moisture content of the entraining/detraining parcel (qcko) and the value it would have if just saturated (qrch), according to equation A.14 in Grell (1993) \cite grell_1993 . Their difference is the amount of convective cloud water (qlk = rain + condensate). Determine the portion of convective cloud water that remains suspended and the portion that is converted into convective precipitation (pwo). Calculate and save the negative cloud work function (aa1) due to water loading. Above the level of minimum moist static energy, some of the cloud water is detrained into the grid-scale cloud water from every cloud layer with a rate of 0.0005 \f$m^{-1}\f$ (dellal). + do k = 2, km1 + do i = 1, im + if (cnvflg(i)) then + if(k > kb(i) .and. k < ktcon(i)) then + dz = zi(i,k) - zi(i,k-1) + gamma = el2orc * qeso(i,k) / (to(i,k)**2) + qrch = qeso(i,k) & + & + gamma * dbyo(i,k) / (hvap * (1. + gamma)) +!j + tem = 0.5 * (xlamue(i,k)+xlamue(i,k-1)) * dz + tem1 = 0.5 * xlamud(i) * dz + factor = 1. + tem - tem1 + qcko(i,k) = ((1.-tem1)*qcko(i,k-1)+tem*0.5* & + & (qo(i,k)+qo(i,k-1)))/factor + qrcko(i,k) = qcko(i,k) +!j + dq = eta(i,k) * (qcko(i,k) - qrch) +! +! rhbar(i) = rhbar(i) + qo(i,k) / qeso(i,k) +! +! below lfc check if there is excess moisture to release latent heat +! + if(k >= kbcon(i) .and. dq > 0.) then + etah = .5 * (eta(i,k) + eta(i,k-1)) + dp = 1000. * del(i,k) + if(ncloud > 0) then + ptem = c0t(i,k) + c1 + qlk = dq / (eta(i,k) + etah * ptem * dz) + dellal(i,k) = etah * c1 * dz * qlk * g / dp + else + qlk = dq / (eta(i,k) + etah * c0t(i,k) * dz) + endif + buo(i,k) = buo(i,k) - g * qlk + qcko(i,k)= qlk + qrch + pwo(i,k) = etah * c0t(i,k) * dz * qlk + cnvwt(i,k) = etah * qlk * g / dp + endif +! +! compute buoyancy and drag for updraft velocity +! + if(k >= kbcon(i)) then + rfact = 1. + delta * cp * gamma & + & * to(i,k) / hvap + buo(i,k) = buo(i,k) + (g / (cp * to(i,k))) & + & * dbyo(i,k) / (1. + gamma) & + & * rfact + val = 0. + buo(i,k) = buo(i,k) + g * delta * & + & max(val,(qeso(i,k) - qo(i,k))) + drag(i,k) = max(xlamue(i,k),xlamud(i)) + endif +! + endif + endif + enddo + enddo +! +! calculate cloud work function +! +! do k = 2, km1 +! do i = 1, im +! if (cnvflg(i)) then +! if(k >= kbcon(i) .and. k < ktcon(i)) then +! dz1 = zo(i,k+1) - zo(i,k) +! gamma = el2orc * qeso(i,k) / (to(i,k)**2) +! rfact = 1. + delta * cp * gamma +! & * to(i,k) / hvap +! aa1(i) = aa1(i) + +!! & dz1 * eta(i,k) * (g / (cp * to(i,k))) +! & dz1 * (g / (cp * to(i,k))) +! & * dbyo(i,k) / (1. + gamma) +! & * rfact +! val = 0. +! aa1(i) = aa1(i) + +!! & dz1 * eta(i,k) * g * delta * +! & dz1 * g * delta * +! & max(val,(qeso(i,k) - qo(i,k))) +! endif +! endif +! enddo +! enddo +! do i = 1, im +! if(cnvflg(i) .and. aa1(i) <= 0.) cnvflg(i) = .false. +! enddo +! +! calculate cloud work function +! +!> - Calculate the cloud work function according to Pan and Wu (1995) \cite pan_and_wu_1995 equation 4: +!! \f[ +!! A_u=\int_{z_0}^{z_t}\frac{g}{c_pT(z)}\frac{\eta}{1 + \gamma}[h(z)-h^*(z)]dz +!! \f] +!! (discretized according to Grell (1993) \cite grell_1993 equation B.10 using B.2 and B.3 of Arakawa and Schubert (1974) \cite arakawa_and_schubert_1974 and assuming \f$\eta=1\f$) where \f$A_u\f$ is the updraft cloud work function, \f$z_0\f$ and \f$z_t\f$ are cloud base and cloud top, respectively, \f$\gamma = \frac{L}{c_p}\left(\frac{\partial \overline{q_s}}{\partial T}\right)_p\f$ and other quantities are previously defined. + do i = 1, im + if (cnvflg(i)) then + aa1(i) = 0. + endif + enddo + do k = 2, km1 + do i = 1, im + if (cnvflg(i)) then + if(k >= kbcon(i) .and. k < ktcon(i)) then + dz1 = zo(i,k+1) - zo(i,k) + aa1(i) = aa1(i) + buo(i,k) * dz1 + endif + endif + enddo + enddo + do i = 1, im + if(cnvflg(i) .and. aa1(i) <= 0.) cnvflg(i) = .false. + enddo +!! +!> - If the updraft cloud work function is negative, convection does not occur, and the scheme returns to the calling routine. + totflg = .true. + do i=1,im + totflg = totflg .and. (.not. cnvflg(i)) + enddo + if(totflg) return +!! +! +! estimate the onvective overshooting as the level +! where the [aafac * cloud work function] becomes zero, +! which is the final cloud top +! limited to the level of P/Ps=0.7 +! +!> - Continue calculating the cloud work function past the point of neutral buoyancy to represent overshooting according to Han and Pan (2011) \cite han_and_pan_2011 . Convective overshooting stops when \f$ cA_u < 0\f$ where \f$c\f$ is currently 10%, or when 10% of the updraft cloud work function has been consumed by the stable buoyancy force. Overshooting is also limited to the level where \f$p=0.7p_{sfc}\f$. + do i = 1, im + if (cnvflg(i)) then + aa1(i) = aafac * aa1(i) + endif + enddo +! + do i = 1, im + flg(i) = cnvflg(i) + ktcon1(i) = kbm(i) + enddo + do k = 2, km1 + do i = 1, im + if (flg(i)) then + if(k >= ktcon(i) .and. k < kbm(i)) then + dz1 = zo(i,k+1) - zo(i,k) + gamma = el2orc * qeso(i,k) / (to(i,k)**2) + rfact = 1. + delta * cp * gamma & + & * to(i,k) / hvap + aa1(i) = aa1(i) + & +! & dz1 * eta(i,k) * (g / (cp * to(i,k))) + & dz1 * (g / (cp * to(i,k))) & + & * dbyo(i,k) / (1. + gamma) & + & * rfact +! val = 0. +! aa1(i) = aa1(i) + +!! & dz1 * eta(i,k) * g * delta * +! & dz1 * g * delta * +! & max(val,(qeso(i,k) - qo(i,k))) + if(aa1(i) < 0.) then + ktcon1(i) = k + flg(i) = .false. + endif + endif + endif + enddo + enddo +! +! compute cloud moisture property, detraining cloud water +! and precipitation in overshooting layers +! +!> - For the overshooting convection, calculate the moisture content of the entraining/detraining parcel as before. Partition convective cloud water and precipitation and detrain convective cloud water in the overshooting layers. + do k = 2, km1 + do i = 1, im + if (cnvflg(i)) then + if(k >= ktcon(i) .and. k < ktcon1(i)) then + dz = zi(i,k) - zi(i,k-1) + gamma = el2orc * qeso(i,k) / (to(i,k)**2) + qrch = qeso(i,k) & + & + gamma * dbyo(i,k) / (hvap * (1. + gamma)) +!j + tem = 0.5 * (xlamue(i,k)+xlamue(i,k-1)) * dz + tem1 = 0.5 * xlamud(i) * dz + factor = 1. + tem - tem1 + qcko(i,k) = ((1.-tem1)*qcko(i,k-1)+tem*0.5* & + & (qo(i,k)+qo(i,k-1)))/factor + qrcko(i,k) = qcko(i,k) +!j + dq = eta(i,k) * (qcko(i,k) - qrch) +! +! check if there is excess moisture to release latent heat +! + if(dq > 0.) then + etah = .5 * (eta(i,k) + eta(i,k-1)) + dp = 1000. * del(i,k) + if(ncloud > 0) then + ptem = c0t(i,k) + c1 + qlk = dq / (eta(i,k) + etah * ptem * dz) + dellal(i,k) = etah * c1 * dz * qlk * g / dp + else + qlk = dq / (eta(i,k) + etah * c0t(i,k) * dz) + endif + qcko(i,k) = qlk + qrch + pwo(i,k) = etah * c0t(i,k) * dz * qlk + cnvwt(i,k) = etah * qlk * g / dp + endif + endif + endif + enddo + enddo +! +! compute updraft velocity square(wu2) +!> - Calculate updraft velocity square(wu2) according to Han et al.'s (2017) \cite han_et_al_2017 equation 7. +! +! bb1 = 2. * (1.+bet1*cd1) +! bb2 = 2. / (f1*(1.+gam1)) +! +! bb1 = 3.9 +! bb2 = 0.67 +! +! bb1 = 2.0 +! bb2 = 4.0 +! + bb1 = 4.0 + bb2 = 0.8 +! +! do i = 1, im +! if (cnvflg(i)) then +! k = kbcon1(i) +! tem = po(i,k) / (rd * to(i,k)) +! wucb = -0.01 * dot(i,k) / (tem * g) +! if(wucb > 0.) then +! wu2(i,k) = wucb * wucb +! else +! wu2(i,k) = 0. +! endif +! endif +! enddo + do k = 2, km1 + do i = 1, im + if (cnvflg(i)) then + if(k > kbcon1(i) .and. k < ktcon(i)) then + dz = zi(i,k) - zi(i,k-1) + tem = 0.25 * bb1 * (drag(i,k)+drag(i,k-1)) * dz + tem1 = 0.5 * bb2 * (buo(i,k)+buo(i,k-1)) * dz + ptem = (1. - tem) * wu2(i,k-1) + ptem1 = 1. + tem + wu2(i,k) = (ptem + tem1) / ptem1 + wu2(i,k) = max(wu2(i,k), 0.) + endif + endif + enddo + enddo +! +! compute updraft velocity averaged over the whole cumulus +! +!> - Calculate the mean updraft velocity within the cloud (wc). + do i = 1, im + wc(i) = 0. + sumx(i) = 0. + enddo + do k = 2, km1 + do i = 1, im + if (cnvflg(i)) then + if(k > kbcon1(i) .and. k < ktcon(i)) then + dz = zi(i,k) - zi(i,k-1) + tem = 0.5 * (sqrt(wu2(i,k)) + sqrt(wu2(i,k-1))) + wc(i) = wc(i) + tem * dz + sumx(i) = sumx(i) + dz + endif + endif + enddo + enddo + do i = 1, im + if(cnvflg(i)) then + if(sumx(i) == 0.) then + cnvflg(i)=.false. + else + wc(i) = wc(i) / sumx(i) + endif + val = 1.e-4 + if (wc(i) < val) cnvflg(i)=.false. + endif + enddo +! +! exchange ktcon with ktcon1 +! + do i = 1, im + if(cnvflg(i)) then + kk = ktcon(i) + ktcon(i) = ktcon1(i) + ktcon1(i) = kk + endif + enddo +! +! this section is ready for cloud water +! + if(ncloud > 0) then +! +! compute liquid and vapor separation at cloud top +! +!> - => Separate the total updraft cloud water at cloud top into vapor and condensate. + do i = 1, im + if(cnvflg(i)) then + k = ktcon(i) - 1 + gamma = el2orc * qeso(i,k) / (to(i,k)**2) + qrch = qeso(i,k) & + & + gamma * dbyo(i,k) / (hvap * (1. + gamma)) + dq = qcko(i,k) - qrch +! +! check if there is excess moisture to release latent heat +! + if(dq > 0.) then + qlko_ktcon(i) = dq + qcko(i,k) = qrch + endif + endif + enddo + endif +! +!--- compute precipitation efficiency in terms of windshear +! +!! - Calculate the wind shear and precipitation efficiency according to equation 58 in Fritsch and Chappell (1980) \cite fritsch_and_chappell_1980 : +!! \f[ +!! E = 1.591 - 0.639\frac{\Delta V}{\Delta z} + 0.0953\left(\frac{\Delta V}{\Delta z}\right)^2 - 0.00496\left(\frac{\Delta V}{\Delta z}\right)^3 +!! \f] +!! where \f$\Delta V\f$ is the integrated horizontal shear over the cloud depth, \f$\Delta z\f$, (the ratio is converted to units of \f$10^{-3} s^{-1}\f$). The variable "edt" is \f$1-E\f$ and is constrained to the range \f$[0,0.9]\f$. + do i = 1, im + if(cnvflg(i)) then + vshear(i) = 0. + endif + enddo + do k = 2, km + do i = 1, im + if (cnvflg(i)) then + if(k > kb(i) .and. k <= ktcon(i)) then + shear= sqrt((uo(i,k)-uo(i,k-1)) ** 2 & + & + (vo(i,k)-vo(i,k-1)) ** 2) + vshear(i) = vshear(i) + shear + endif + endif + enddo + enddo + do i = 1, im + if(cnvflg(i)) then + vshear(i) = 1.e3 * vshear(i) / (zi(i,ktcon(i))-zi(i,kb(i))) + e1=1.591-.639*vshear(i) & + & +.0953*(vshear(i)**2)-.00496*(vshear(i)**3) + edt(i)=1.-e1 + val = .9 + edt(i) = min(edt(i),val) + val = .0 + edt(i) = max(edt(i),val) + endif + enddo +! +!--- what would the change be, that a cloud with unit mass +!--- will do to the environment? +! +!> ## Calculate the tendencies of the state variables (per unit cloud base mass flux) and the cloud base mass flux. +!> - Calculate the change in moist static energy, moisture mixing ratio, and horizontal winds per unit cloud base mass flux for all layers below cloud top from equations B.14 and B.15 from Grell (1993) \cite grell_1993, and for the cloud top from B.16 and B.17. + !$ser mode write + !$ser savepoint QUOTATION samfshalcnv-part3-input QUOTATION + !$ser data ix=ix im=im km=km + !$ser data dellal=dellal !output + !$ser data delp=delp garea=garea qtr=qtr u1=u1 v1=v1 + !$ser data kb=kb kbcon=kbcon kbcon1=kbcon1 ktcon=ktcon ktcon1=ktcon1 + !$ser data kmax=kmax del=del umean=umean tauadv=tauadv gdx=gdx dtconv=dtconv + !$ser data po=po xlamud=xlamud xmb=xmb xmbmax=xmbmax to=to qo=qo uo=uo vo=vo + !$ser data ctro=ctro wc=wc scaldfunc=scaldfunc sigmagfm=sigmagfm + !$ser data qlko_ktcon=qlko_ktcon xlamue=xlamue heo=heo + !$ser data dellah=dellah dellaq=dellaq dellae=dellae dellau=dellau dellav=dellav + !$ser data hcko=hcko ucko=ucko vcko=vcko qcko=qcko qrcko=qrcko ecko=ecko + !$ser data eta=eta zi=zi c0t=c0t sumx=sumx cnvflg=cnvflg + do k = 1, km + do i = 1, im + if(cnvflg(i) .and. k <= kmax(i)) then + dellah(i,k) = 0. + dellaq(i,k) = 0. + dellau(i,k) = 0. + dellav(i,k) = 0. + endif + enddo + enddo + do n = 1, ntr + do k = 1, km + do i = 1, im + if(cnvflg(i) .and. k <= kmax(i)) then + dellae(i,k,n) = 0. + endif + enddo + enddo + enddo +! +!--- changed due to subsidence and entrainment +! + do k = 2, km1 + do i = 1, im + if (cnvflg(i)) then + if(k > kb(i) .and. k < ktcon(i)) then + dp = 1000. * del(i,k) + dz = zi(i,k) - zi(i,k-1) +! + dv1h = heo(i,k) + dv2h = .5 * (heo(i,k) + heo(i,k-1)) + dv3h = heo(i,k-1) + dv1q = qo(i,k) + dv2q = .5 * (qo(i,k) + qo(i,k-1)) + dv3q = qo(i,k-1) +! + tem = 0.5 * (xlamue(i,k)+xlamue(i,k-1)) + tem1 = xlamud(i) +!j + dellah(i,k) = dellah(i,k) + & + & ( eta(i,k)*dv1h - eta(i,k-1)*dv3h & + & - tem*eta(i,k-1)*dv2h*dz & + & + tem1*eta(i,k-1)*.5*(hcko(i,k)+hcko(i,k-1))*dz & + & ) *g/dp +!j + dellaq(i,k) = dellaq(i,k) + & + & ( eta(i,k)*dv1q - eta(i,k-1)*dv3q & + & - tem*eta(i,k-1)*dv2q*dz & + & + tem1*eta(i,k-1)*.5*(qrcko(i,k)+qcko(i,k-1))*dz & + & ) *g/dp +!j + tem1=eta(i,k)*(uo(i,k)-ucko(i,k)) + tem2=eta(i,k-1)*(uo(i,k-1)-ucko(i,k-1)) + dellau(i,k) = dellau(i,k) + (tem1-tem2) * g/dp +!j + tem1=eta(i,k)*(vo(i,k)-vcko(i,k)) + tem2=eta(i,k-1)*(vo(i,k-1)-vcko(i,k-1)) + dellav(i,k) = dellav(i,k) + (tem1-tem2) * g/dp +!j + endif + endif + enddo + enddo + do n = 1, ntr + do k = 2, km1 + do i = 1, im + if (cnvflg(i)) then + if(k > kb(i) .and. k < ktcon(i)) then + dp = 1000. * del(i,k) +!j + tem1=eta(i,k)*(ctro(i,k,n)-ecko(i,k,n)) + tem2=eta(i,k-1)*(ctro(i,k-1,n)-ecko(i,k-1,n)) + dellae(i,k,n) = dellae(i,k,n) + (tem1-tem2) * g/dp +!j + endif + endif + enddo + enddo + enddo +! +!------- cloud top +! + do i = 1, im + if(cnvflg(i)) then + indx = ktcon(i) + dp = 1000. * del(i,indx) + dv1h = heo(i,indx-1) + dellah(i,indx) = eta(i,indx-1) * & + & (hcko(i,indx-1) - dv1h) * g / dp + dv1q = qo(i,indx-1) + dellaq(i,indx) = eta(i,indx-1) * & + & (qcko(i,indx-1) - dv1q) * g / dp + dellau(i,indx) = eta(i,indx-1) * & + & (ucko(i,indx-1) - uo(i,indx-1)) * g / dp + dellav(i,indx) = eta(i,indx-1) * & + & (vcko(i,indx-1) - vo(i,indx-1)) * g / dp +! +! cloud water +! + dellal(i,indx) = eta(i,indx-1) * & + & qlko_ktcon(i) * g / dp + endif + enddo + do n = 1, ntr + do i = 1, im + if(cnvflg(i)) then + indx = ktcon(i) + dp = 1000. * del(i,indx) + dellae(i,indx,n) = eta(i,indx-1) * & + & (ecko(i,indx-1,n) - ctro(i,indx-1,n)) * g / dp + endif + enddo + enddo +! +! compute convective turn-over time +! +!> - Following Bechtold et al. (2008) \cite bechtold_et_al_2008, calculate the convective turnover time using the mean updraft velocity (wc) and the cloud depth. It is also proportional to the grid size (gdx). + do i= 1, im + if(cnvflg(i)) then + tem = zi(i,ktcon1(i)) - zi(i,kbcon1(i)) + dtconv(i) = tem / wc(i) + tfac = 1. + gdx(i) / 75000. + dtconv(i) = tfac * dtconv(i) + dtconv(i) = max(dtconv(i),dtmin) + dtconv(i) = max(dtconv(i),dt2) + dtconv(i) = min(dtconv(i),dtmax) + endif + enddo +! +!> - Calculate advective time scale (tauadv) using a mean cloud layer wind speed. + do i= 1, im + if(cnvflg(i)) then + sumx(i) = 0. + umean(i) = 0. + endif + enddo + do k = 2, km1 + do i = 1, im + if(cnvflg(i)) then + if(k >= kbcon1(i) .and. k < ktcon1(i)) then + dz = zi(i,k) - zi(i,k-1) + tem = sqrt(u1(i,k)*u1(i,k)+v1(i,k)*v1(i,k)) + umean(i) = umean(i) + tem * dz + sumx(i) = sumx(i) + dz + endif + endif + enddo + enddo + do i= 1, im + if(cnvflg(i)) then + umean(i) = umean(i) / sumx(i) + umean(i) = max(umean(i), 1.) + tauadv(i) = gdx(i) / umean(i) + endif + enddo +! +! compute cloud base mass flux as a function of the mean +! updraft velcoity +! +!> - From Han et al.'s (2017) \cite han_et_al_2017 equation 6, calculate cloud base mass flux as a function of the mean updraft velcoity. +!! As discussed in Han et al. (2017) \cite han_et_al_2017 , when dtconv is larger than tauadv, the convective mixing is not fully conducted before the cumulus cloud is advected out of the grid cell. In this case, therefore, the cloud base mass flux is further reduced in proportion to the ratio of tauadv to dtconv. + do i= 1, im + if(cnvflg(i)) then + k = kbcon(i) + rho = po(i,k)*100. / (rd*to(i,k)) + tfac = tauadv(i) / dtconv(i) + tfac = min(tfac, 1.) + xmb(i) = tfac*betaw*rho*wc(i) + endif + enddo +! +!> - For scale-aware parameterization, the updraft fraction (sigmagfm) is first computed as a function of the lateral entrainment rate at cloud base (see Han et al.'s (2017) \cite han_et_al_2017 equation 4 and 5), following the study by Grell and Freitas (2014) \cite grell_and_freitus_2014. + do i = 1, im + if(cnvflg(i)) then + tem = min(max(xlamue(i,kbcon(i)), 2.e-4), 6.e-4) + tem = 0.2 / tem + tem1 = 3.14 * tem * tem + sigmagfm(i) = tem1 / garea(i) + sigmagfm(i) = max(sigmagfm(i), 0.001) + sigmagfm(i) = min(sigmagfm(i), 0.999) + endif + enddo +! +!> - Then, calculate the reduction factor (scaldfunc) of the vertical convective eddy transport of mass flux as a function of updraft fraction from the studies by Arakawa and Wu (2013) \cite arakawa_and_wu_2013 (also see Han et al.'s (2017) \cite han_et_al_2017 equation 1 and 2). The final cloud base mass flux with scale-aware parameterization is obtained from the mass flux when sigmagfm << 1, multiplied by the reduction factor (Han et al.'s (2017) \cite han_et_al_2017 equation 2). + do i = 1, im + if(cnvflg(i)) then + if (gdx(i) < dxcrt) then + scaldfunc(i) = (1.-sigmagfm(i)) * (1.-sigmagfm(i)) + scaldfunc(i) = max(min(scaldfunc(i), 1.0), 0.) + else + scaldfunc(i) = 1.0 + endif + xmb(i) = xmb(i) * scaldfunc(i) + xmb(i) = min(xmb(i),xmbmax(i)) + endif + enddo + +! +! transport aerosols if present +! +! if (do_aerosols) & +! & call samfshalcnv_aerosols(im, ix, km, itc, ntc, ntr, delt, & +!! & xlamde, xlamdd, cnvflg, jmin, kb, kmax, kbcon, ktcon, fscav, +! & cnvflg, kb, kmax, kbcon, ktcon, fscav, & +!! & edto, xlamd, xmb, c0t, eta, etad, zi, xlamue, xlamud, delp, +! & xmb, c0t, eta, zi, xlamue, xlamud, delp, & +! & qtr, qaero) + +!> ## For the "feedback control", calculate updated values of the state variables by multiplying the cloud base mass flux and the tendencies calculated per unit cloud base mass flux from the static control. +!! - Recalculate saturation specific humidity. +! +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +! + !$ser mode write + !$ser savepoint QUOTATION samfshalcnv-part4-input QUOTATION + !$ser data ix=ix im=im km=km + !$ser data cnvw=cnvw dt_mf=dt_mf kbot=kbot kcnv=kcnv + !$ser data islimsk=islimsk qtr=qtr q1=q1 t1=t1 u1=u1 v1=v1 + !$ser data ktop=ktop rn=rn cnvc=cnvc ud_mf=ud_mf kb=kb + !$ser data kbcon=kbcon ktcon=ktcon kmax=kmax del=del delhbar=delhbar + !$ser data delq=delq delq2=delq2 delqbar=delqbar delqev=delqev + !$ser data deltbar=deltbar deltv=deltv edt=edt qcond=qcond qevap=qevap + !$ser data rntot=rntot xmb=xmb delebar=delebar delubar=delubar delvbar=delvbar + !$ser data pfld=pfld qeso=qeso ctr=ctr sigmagfm=sigmagfm + !$ser data dellal=dellal dellah=dellah dellaq=dellaq dellae=dellae dellau=dellau + !$ser data dellav=dellav eta=eta pwo=pwo cnvwt=cnvwt cnvflg=cnvflg flg=flg + do k = 1, km + do i = 1, im + if (cnvflg(i) .and. k <= kmax(i)) then + qeso(i,k) = 0.01 * fpvs(t1(i,k)) ! fpvs is in pa + qeso(i,k) = eps * qeso(i,k) / (pfld(i,k) + epsm1*qeso(i,k)) + val = 1.e-8 + qeso(i,k) = max(qeso(i,k), val ) + endif + enddo + enddo +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +! +!> - Calculate the temperature tendency from the moist static energy and specific humidity tendencies. +!> - Update the temperature, specific humidity, and horiztonal wind state variables by multiplying the cloud base mass flux-normalized tendencies by the cloud base mass flux. +!> - Accumulate column-integrated tendencies. + do i = 1, im + delhbar(i) = 0. + delqbar(i) = 0. + deltbar(i) = 0. + delubar(i) = 0. + delvbar(i) = 0. + qcond(i) = 0. + enddo + do n = 1, ntr + do i = 1, im + delebar(i,n) = 0. + enddo + enddo + do k = 1, km + do i = 1, im + if (cnvflg(i)) then + if(k > kb(i) .and. k <= ktcon(i)) then + dellat = (dellah(i,k) - hvap * dellaq(i,k)) / cp + t1(i,k) = t1(i,k) + dellat * xmb(i) * dt2 + q1(i,k) = q1(i,k) + dellaq(i,k) * xmb(i) * dt2 +! tem = 1./rcs(i) +! u1(i,k) = u1(i,k) + dellau(i,k) * xmb(i) * dt2 * tem +! v1(i,k) = v1(i,k) + dellav(i,k) * xmb(i) * dt2 * tem + u1(i,k) = u1(i,k) + dellau(i,k) * xmb(i) * dt2 + v1(i,k) = v1(i,k) + dellav(i,k) * xmb(i) * dt2 + dp = 1000. * del(i,k) + delhbar(i) = delhbar(i) + dellah(i,k)*xmb(i)*dp/g + delqbar(i) = delqbar(i) + dellaq(i,k)*xmb(i)*dp/g + deltbar(i) = deltbar(i) + dellat*xmb(i)*dp/g + delubar(i) = delubar(i) + dellau(i,k)*xmb(i)*dp/g + delvbar(i) = delvbar(i) + dellav(i,k)*xmb(i)*dp/g + endif + endif + enddo + enddo + do n = 1, ntr + kk = n+2 + do k = 1, km + do i = 1, im + if (cnvflg(i) .and. k <= kmax(i)) then + if(k <= ktcon(i)) then + ctr(i,k,n) = ctr(i,k,n)+dellae(i,k,n)*xmb(i)*dt2 + delebar(i,n)=delebar(i,n)+dellae(i,k,n)*xmb(i)*dp/g + qtr(i,k,kk) = ctr(i,k,n) + endif + endif + enddo + enddo + enddo +! +!> - Recalculate saturation specific humidity using the updated temperature. + do k = 1, km + do i = 1, im + if (cnvflg(i)) then + if(k > kb(i) .and. k <= ktcon(i)) then + qeso(i,k) = 0.01 * fpvs(t1(i,k)) ! fpvs is in pa + qeso(i,k) = eps * qeso(i,k)/(pfld(i,k) + epsm1*qeso(i,k)) + val = 1.e-8 + qeso(i,k) = max(qeso(i,k), val ) + endif + endif + enddo + enddo +! +!> - Add up column-integrated convective precipitation by multiplying the normalized value by the cloud base mass flux. + do i = 1, im + rntot(i) = 0. + delqev(i) = 0. + delq2(i) = 0. + flg(i) = cnvflg(i) + enddo + do k = km, 1, -1 + do i = 1, im + if (cnvflg(i)) then + if(k < ktcon(i) .and. k > kb(i)) then + rntot(i) = rntot(i) + pwo(i,k) * xmb(i) * .001 * dt2 + endif + endif + enddo + enddo +! +! evaporating rain +! +!> - Determine the evaporation of the convective precipitation and update the integrated convective precipitation. +!> - Update state temperature and moisture to account for evaporation of convective precipitation. +!> - Update column-integrated tendencies to account for evaporation of convective precipitation. + do k = km, 1, -1 + do i = 1, im + if (k <= kmax(i)) then + deltv(i) = 0. + delq(i) = 0. + qevap(i) = 0. + if(cnvflg(i)) then + if(k < ktcon(i) .and. k > kb(i)) then + rn(i) = rn(i) + pwo(i,k) * xmb(i) * .001 * dt2 + endif + endif + if(flg(i) .and. k < ktcon(i)) then + evef = edt(i) * evfact + if(islimsk(i) == 1) evef=edt(i) * evfactl +! if(islimsk(i) == 1) evef=.07 +! if(islimsk(i) == 1) evef = 0. + qcond(i) = evef * (q1(i,k) - qeso(i,k)) & + & / (1. + el2orc * qeso(i,k) / t1(i,k)**2) + dp = 1000. * del(i,k) + if(rn(i) > 0. .and. qcond(i) < 0.) then + qevap(i) = -qcond(i) * (1.-exp(-.32*sqrt(dt2*rn(i)))) + qevap(i) = min(qevap(i), rn(i)*1000.*g/dp) + delq2(i) = delqev(i) + .001 * qevap(i) * dp / g + endif + if(rn(i) > 0. .and. qcond(i) < 0. .and. & + & delq2(i) > rntot(i)) then + qevap(i) = 1000.* g * (rntot(i) - delqev(i)) / dp + flg(i) = .false. + endif + if(rn(i) > 0. .and. qevap(i) > 0.) then + tem = .001 * dp / g + tem1 = qevap(i) * tem + if(tem1 > rn(i)) then + qevap(i) = rn(i) / tem + rn(i) = 0. + else + rn(i) = rn(i) - tem1 + endif + q1(i,k) = q1(i,k) + qevap(i) + t1(i,k) = t1(i,k) - elocp * qevap(i) + deltv(i) = - elocp*qevap(i)/dt2 + delq(i) = + qevap(i)/dt2 + delqev(i) = delqev(i) + .001*dp*qevap(i)/g + endif + delqbar(i) = delqbar(i) + delq(i)*dp/g + deltbar(i) = deltbar(i) + deltv(i)*dp/g + endif + endif + enddo + enddo +!j +! do i = 1, im +! if(me == 31 .and. cnvflg(i)) then +! if(cnvflg(i)) then +! print *, ' shallow delhbar, delqbar, deltbar = ', +! & delhbar(i),hvap*delqbar(i),cp*deltbar(i) +! print *, ' shallow delubar, delvbar = ',delubar(i),delvbar(i) +! print *, ' precip =', hvap*rn(i)*1000./dt2 +! print*,'pdif= ',pfld(i,kbcon(i))-pfld(i,ktcon(i)) +! endif +! enddo +! do n = 1, ntr +! do i = 1, im +! if(me == 31 .and. cnvflg(i)) then +! if(cnvflg(i)) then +! print *, ' tracer delebar = ',delebar(i,n) +! endif +! enddo +! enddo +!j + do i = 1, im + if(cnvflg(i)) then + if(rn(i) < 0. .or. .not.flg(i)) rn(i) = 0. + ktop(i) = ktcon(i) + kbot(i) = kbcon(i) + kcnv(i) = 2 + endif + enddo +! +! convective cloud water +! +!> - Calculate shallow convective cloud water. + do k = 1, km + do i = 1, im + if (cnvflg(i)) then + if (k >= kbcon(i) .and. k < ktcon(i)) then + cnvw(i,k) = cnvwt(i,k) * xmb(i) * dt2 + endif + endif + enddo + enddo + +! +! convective cloud cover +! +!> - Calculate convective cloud cover, which is used when pdf-based cloud fraction is used (i.e., pdfcld=.true.). + do k = 1, km + do i = 1, im + if (cnvflg(i)) then + if (k >= kbcon(i) .and. k < ktcon(i)) then + cnvc(i,k) = 0.04 * log(1. + 675. * eta(i,k) * xmb(i)) + cnvc(i,k) = min(cnvc(i,k), 0.2) + cnvc(i,k) = max(cnvc(i,k), 0.0) + endif + endif + enddo + enddo +! +! cloud water +! +!> - Separate detrained cloud water into liquid and ice species as a function of temperature only. + if (ncloud > 0) then +! + do k = 1, km1 + do i = 1, im + if (cnvflg(i)) then +! if (k > kb(i) .and. k <= ktcon(i)) then + if (k >= kbcon(i) .and. k <= ktcon(i)) then + tem = dellal(i,k) * xmb(i) * dt2 + tem1 = max(0.0, min(1.0, (tcr-t1(i,k))*tcrf)) + if (qtr(i,k,2) > -999.0) then + qtr(i,k,1) = qtr(i,k,1) + tem * tem1 ! ice + qtr(i,k,2) = qtr(i,k,2) + tem *(1.0-tem1) ! water + else + qtr(i,k,1) = qtr(i,k,1) + tem + endif + endif + endif + enddo + enddo +! + endif +!> - Store aerosol concentrations if present + if (do_aerosols) then + do n = 1, ntc + kk = n + itc - 1 + do k = 1, km + do i = 1, im + if(cnvflg(i) .and. rn(i) > 0.) then + if (k <= kmax(i)) qtr(i,k,kk) = qaero(i,k,n) + endif + enddo + enddo + enddo + endif +! +! hchuang code change +! +!> - Calculate and retain the updraft mass flux for dust transport by cumulus convection. +! +!> - Calculate the updraft convective mass flux. + do k = 1, km + do i = 1, im + if(cnvflg(i)) then + if(k >= kb(i) .and. k < ktop(i)) then + ud_mf(i,k) = eta(i,k) * xmb(i) * dt2 + endif + endif + enddo + enddo +!> - save the updraft convective mass flux at cloud top. + do i = 1, im + if(cnvflg(i)) then + k = ktop(i)-1 + dt_mf(i,k) = ud_mf(i,k) + endif + enddo +! +! include TKE contribution from shallow convection +! + if (ntk > 0) then +! + do k = 2, km1 + do i = 1, im + if(cnvflg(i)) then + if(k > kb(i) .and. k < ktop(i)) then + tem = 0.5 * (eta(i,k-1) + eta(i,k)) * xmb(i) + tem1 = pfld(i,k) * 100. / (rd * t1(i,k)) + sigmagfm(i) = max(sigmagfm(i), betaw) + ptem = tem / (sigmagfm(i) * tem1) + qtr(i,k,ntk)=qtr(i,k,ntk)+0.5*sigmagfm(i)*ptem*ptem + endif + endif + enddo + enddo +! + endif + !ser cleanup +!! + return + end +end program samfshalconv_serialize diff --git a/projects2020/group05/tests/read_serialization.py b/projects2020/group05/tests/read_serialization.py new file mode 100644 index 00000000..633594c2 --- /dev/null +++ b/projects2020/group05/tests/read_serialization.py @@ -0,0 +1,104 @@ +from shalconv import DATAPATH +from shalconv.serialization import data_dict_from_var_list, numpy_dict_to_gt4py_dict +import serialbox as ser + + +def read_serialization_partx(var_list, part, tile = 0, path = DATAPATH): + serializer = ser.Serializer(ser.OpenModeKind.Read, path, "Serialized_rank"+str(tile)) + sp = ser.Savepoint(f"samfshalcnv-part{part}-input") + data = data_dict_from_var_list(var_list, serializer, sp) + + return numpy_dict_to_gt4py_dict(data) + + +def read_serialization_part2_x(var_list, part, tile = 0, path = DATAPATH): + serializer = ser.Serializer(ser.OpenModeKind.Read, path, "Serialized_rank"+str(tile)) + sp = ser.Savepoint(f"samfshalcnv-part2-{part}") + data = data_dict_from_var_list(var_list, serializer, sp) + + return numpy_dict_to_gt4py_dict(data) + + +def read_serialization_part2(): + var_list = ['ix', 'km', 'islimsk', 'dot', 'qtr', 'kpbl', 'kb', 'kbcon', 'kbcon1', + 'cnvwt', 'dellal', 'ktconn', 'pwo', 'qlko_ktcon', 'qrcko', 'xmbmax', #output + 'ktcon', 'ktcon1', 'kbm', 'kmax', 'aa1', 'cina', 'tkemean', + 'clamt', 'del', 'edt', 'pdot', 'po', 'hmax', 'vshear', 'xlamud', + 'pfld', 'to', 'qo', 'uo', 'vo', 'qeso', 'ctro', 'wu2', 'buo', + 'drag', 'wc', 'dbyo', 'zo', 'xlamue', 'heo', 'heso', 'hcko', + 'ucko', 'vcko', 'qcko', 'ecko', 'eta', 'zi', 'c0t', 'sumx', + 'cnvflg', 'flg'] + + return read_serialization_partx(var_list, 2) + + +def read_serialization_part2_1(): + var_list = ['ix','km','hmax', 'heo', 'heso', 'kb', 'to', 'qeso', 'po', 'qo', 'uo', 'vo'] + + return read_serialization_part2_x(var_list, 1) + + +def read_serialization_part2_2(): + var_list = ['ix','km','cnvflg','pdot'] + + return read_serialization_part2_x(var_list, 2) + + +def read_serialization_part2_3(): + var_list = ['ix','km','clamt','xlamud','xlamue','eta','kmax','kbm','hcko','ucko','vcko','tkemean','sumx', + 'cnvflg','kb','zi','heo','dbyo','heso','pgcon','uo','vo'] + + return read_serialization_part2_x(var_list, 3) + + +def read_serialization_part2_4(): + var_list = ['ix','km','hcko','dbyo','ucko','vcko', + 'cnvflg','kmax','kbm','kbcon','kbcon1','flg','pfld'] + + return read_serialization_part2_x(var_list, 4) + + +def read_serialization_part2_5(): + var_list = ['ix','km','cnvflg','kbcon1','flg', + 'cina','kb','zo','qeso','to','dbyo','qo','pdot','islimsk'] + + return read_serialization_part2_x(var_list, 5) + + +def read_serialization_part2_6(): + var_list = ['ix','km','cnvflg','cina'] + + return read_serialization_part2_x(var_list, 6) + + +def read_serialization_part3(): + var_list = ['ix', 'km', 'delp', 'garea', 'qtr', 'u1', 'v1', 'kb', + 'kbcon', 'kbcon1', 'ktcon', 'ktcon1', 'kmax', 'del', + 'umean', 'tauadv', 'gdx', 'dtconv', 'po', 'xlamud', + 'xmb', 'xmbmax', 'to', 'qo', 'uo', 'vo', 'ctro', #'qaero', + 'wc', 'scaldfunc', 'sigmagfm', 'qlko_ktcon', 'xlamue', + 'heo', 'dellah', 'dellaq', 'dellae', 'dellau', 'dellav', 'dellal', + 'hcko', 'ucko', 'vcko', 'qcko', 'qrcko', 'ecko', 'eta', + 'zi', 'c0t', 'sumx', 'cnvflg'] + + return read_serialization_partx(var_list, 3) + + +def read_serialization_part4(): + var_list = ['ix', 'km', 'islimsk', 'qtr', 'q1', 't1', 'u1', 'v1', + 'cnvw', 'dt_mf', 'kbot', 'kcnv', #output + 'ktop', 'rn', 'cnvc', 'ud_mf', 'kb', 'kbcon', 'ktcon', + 'kmax', 'del', 'delhbar', 'delq', 'delq2', 'delqbar', + 'delqev', 'deltbar', 'deltv', 'edt', 'qcond', 'qevap', + 'rntot', 'xmb', 'delubar', 'delvbar', 'pfld', #delebar(ix,ntr) + 'qeso', 'ctr', 'sigmagfm', 'dellal', 'dellah', #'qaero', + 'dellaq', 'dellae', 'dellau', 'dellav', 'eta', 'pwo', + 'cnvwt', 'cnvflg', 'flg'] + + return read_serialization_partx(var_list, 4) + + +if __name__ == "__main__": + read_serialization_part2() + read_serialization_part3() + read_serialization_part4() diff --git a/projects2020/group05/tests/run_serialization.py b/projects2020/group05/tests/run_serialization.py new file mode 100644 index 00000000..9170d68a --- /dev/null +++ b/projects2020/group05/tests/run_serialization.py @@ -0,0 +1,44 @@ +import sys, os +from shalconv import DATAPATH, SERIALBOX_DIR, ISDOCKER +sys.path.append(SERIALBOX_DIR + "/python") +import serialbox as ser + +if "NETCDF_LIB" in os.environ: + NETCDF_LIB = os.environ["NETCDF_LIB"] +else: + NETCDF_LIB = "/usr/lib/x86_64-linux-gnu" + +FFLAGS = f"-cpp -fdec -fdefault-real-8 -fno-fast-math -ffree-form -ffree-line-length-none \ + -fno-backslash -fimplicit-none -frange-check -pedantic -Waliasing -Wampersand \ + -Wline-truncation -Wsurprising -Wtabs -Wunderflow -O0 -g -fbacktrace -fdump-core \ + -ffpe-trap=invalid,zero,overflow -fbounds-check -finit-real=nan -finit-integer=9999999 \ + -finit-logical=true -finit-character=35 -DSERIALIZE -I{SERIALBOX_DIR}/include" + +LDFLAGS = f"{SERIALBOX_DIR}/lib/libSerialboxFortran.a {SERIALBOX_DIR}/lib/libSerialboxC.a \ + {SERIALBOX_DIR}/lib/libSerialboxCore.a -L/lib/x86_64-linux-gnu -L{NETCDF_LIB} \ + -lnetcdff -lnetcdf -lpthread -lstdc++ -lstdc++fs" + +datapath = DATAPATH +outputfile = "fortran/samfshalconv_generated.f90" +templatefile = "fortran/samfshalconv_serialize.template.f90" +inputfile = "fortran/samfshalconv_serialize.f90" +objfile = "fortran/samfshalconv_generated.o" +targetfile = "fortran/samfshalconv_generated.x" + +with open(templatefile,"r") as f: + + filestr = f.read().replace("DATAPATH",datapath) + if ISDOCKER: + filestr = filestr.replace(" QUOTATION ",'"') + filestr = filestr.replace(" QUOTATION", '"') + else: + filestr = filestr.replace("QUOTATION", '') + + with open(inputfile,"w") as fw: + fw.write(filestr) + +os.system(f"{SERIALBOX_DIR}/python/pp_ser/pp_ser.py --no-prefix -v --output={outputfile} {inputfile}") +os.system(f"gfortran {FFLAGS} -c {outputfile} -o {objfile} -DDATPATH='{datapath}'") +os.system(f"gfortran {objfile} {LDFLAGS} -o {targetfile}") +os.system(f"./{targetfile}") +#os.system(f"f2py --f2cmap fortran/.f2py_f2cmap -c -m samfshalconv_ser {outputfile} --f90flags='{FFLAGS}' {F2PYFLAGS}") diff --git a/projects2020/group05/tests/test_fpvsx.py b/projects2020/group05/tests/test_fpvsx.py new file mode 100644 index 00000000..3239c24e --- /dev/null +++ b/projects2020/group05/tests/test_fpvsx.py @@ -0,0 +1,76 @@ +import numpy as np + +import gt4py +import gt4py.gtscript as gtscript +import gt4py.storage as gt_storage +from gt4py.gtscript import PARALLEL, computation, interval +import sys +sys.path.append("..") +from shalconv.funcphys import fpvsx, fpvsx_gt + +backend="numpy" # "debug", "numpy", "gtx86", "gtcuda" +dtype = np.float64 + +con_ttp = 2.7316e+2 # Temperature at H2O 3pt +con_psat = 6.1078e+2 # Pressure at H2O 3pt +con_rv = 4.6150e+2 # Gas constant H2O +con_cvap = 1.8460e+3 # Specific heat of H2O gas +con_cliq = 4.1855e+3 # Specific heat of H2O liquid +con_csol = 2.1060e+3 # Specific heat of H2O ice +con_hvap = 2.5000e+6 # Latent heat of H2O condensation +con_hfus = 3.3358e+5 # Latent heat of H2O fusion +con_dldtl = con_cvap - con_cliq +con_dldti = con_cvap - con_csol +con_xponal = -con_dldtl/con_rv +con_xponbl = -con_dldtl/con_rv + con_hvap/(con_rv * con_ttp) +con_xponai = -con_dldti/con_rv +con_xponbi = -con_dldti/con_rv + (con_hvap + con_hfus)/(con_rv * con_ttp) + +def fpvsx(t): + tr = con_ttp / t + tliq = con_ttp + tice = con_ttp - 20.0 + + w = (t - tice) / (tliq - tice) + pvl = con_psat * (tr ** con_xponal) * np.exp(con_xponbl * (1.0 - tr)) + pvi = con_psat * (tr ** con_xponai) * np.exp(con_xponbi * (1.0 - tr)) + res = np.zeros_like(t) + res = np.where(t >= tliq, pvl, res) + res = np.where(t < tice, pvi, res) + res = np.where((t=tice), w * pvl + (1.0 - w) * pvi, res) + return res + +@gtscript.stencil(backend=backend) # this decorator triggers compilation of the stencil +def fpvsx_stencil(t: gtscript.Field[dtype],res: gtscript.Field[dtype]): + with computation(PARALLEL), interval(...): + tr = con_ttp / t + tliq = con_ttp + tice = con_ttp - 20.0 + + tmp_l = np.e ** (con_xponbl * (1.0 - tr)) + tmp_i = np.e ** (con_xponbi * (1.0 - tr)) + res = 0.0 + w = 0.0 + pvl = 0.0 + pvi = 0.0 + if t >= tliq: + res = con_psat * (tr ** con_xponal) * tmp_l + elif t < tice: + res = con_psat * (tr ** con_xponai) * tmp_i + else: + w = (t - tice) / (tliq - tice) + pvl = con_psat * (tr ** con_xponal) * tmp_l + pvi = con_psat * (tr ** con_xponai) * tmp_i + + res = w * pvl + (1.0 - w) * pvi + +shape = (10,10,10) +x = np.random.rand(*shape) + 10.5 +y = np.zeros(shape) +x_gt = gt4py.storage.from_array(x, backend, (0,0,0), dtype=dtype) +y_gt = gt4py.storage.from_array(y, backend, (0,0,0), dtype=dtype) + +fpvsx_stencil(x_gt, y_gt, domain = shape) +y_gt.synchronize() +y[...] = fpvsx(x) +print(np.allclose(y,y_gt.view(np.ndarray))) diff --git a/projects2020/group05/tests/test_gt4py.py b/projects2020/group05/tests/test_gt4py.py new file mode 100644 index 00000000..78ad2374 --- /dev/null +++ b/projects2020/group05/tests/test_gt4py.py @@ -0,0 +1,29 @@ +import numpy as np + +import gt4py +import gt4py.gtscript as gtscript +import gt4py.storage as gt_storage +from gt4py.gtscript import PARALLEL, computation, interval +import sys +sys.path.append("..") +from shalconv.kernels.utils import exp + + +backend="numpy" # "debug", "numpy", "gtx86", "gtcuda" +dtype = np.float64 + +@gtscript.function +def f(x): + return x + +@gtscript.stencil(backend=backend, externals={"f":f, "exp":exp}) # this decorator triggers compilation of the stencil +def stencil_test(data: gtscript.Field[dtype]): + with computation(PARALLEL), interval(...): + x = exp(0) + y = 0.0 + if 1: + y = f(x) + +data = gt4py.storage.empty(backend, (0,0,0), (10,10,10), dtype=dtype) + +stencil_test(data, domain = (10,10,10)) diff --git a/projects2020/group05/tests/test_misc.py b/projects2020/group05/tests/test_misc.py new file mode 100644 index 00000000..619a33ee --- /dev/null +++ b/projects2020/group05/tests/test_misc.py @@ -0,0 +1,43 @@ +import pytest +import numpy as np +import sys +sys.path.append("..") +from shalconv.serialization import read_data, compare_data, OUT_VARS +from shalconv.funcphys import * + +def test_read_inputvsinput(): + for i in range(6): + data = read_data(i,"in") + compare_data(data, data) + +@pytest.mark.xfail +def test_read_inputvsoutput(): #should FAIL! + for i in range(6): + in_data = read_data(i,"in") + out_data = read_data(i, "out") + in_data_filtered = {k:in_data[k] for k in OUT_VARS} + compare_data(in_data_filtered, out_data) + +def test_fpvs_lookupvsexact(): + # Initialize look-up table + gpvs() + + # Compute fpvs values + n = 100 + rand_temp = np.linspace(180.0, 330.0, n) + fpvs_arr = np.empty(n) + fpvsx_arr = np.empty(n) + + for i in range(0, n): + t = rand_temp[i] + fpvs_arr[i] = fpvs(t) + fpvsx_arr[i] = fpvsx(t) + print(fpvs_arr[i], fpvsx_arr[i]) + + # Validate + if np.allclose(fpvs_arr, fpvsx_arr): + print("Values are correct according to accuracy loss expected from interpolation!") + else: + print("Not all values are validated!") + + diff --git a/projects2020/group05/tests/test_part1.py b/projects2020/group05/tests/test_part1.py new file mode 100644 index 00000000..7d390118 --- /dev/null +++ b/projects2020/group05/tests/test_part1.py @@ -0,0 +1,325 @@ +import pytest +import gt4py as gt +from gt4py import gtscript +import sys +sys.path.append("..") +from shalconv.kernels.stencils_part1 import * +from read_serialization import * +from shalconv.serialization import * +from shalconv import * +from shalconv.physcons import ( + con_g as grav, + con_cp as cp, + con_hvap as hvap, + con_rv as rv, + con_fvirt as fv, + con_t0c as t0c, + con_rd as rd, + con_cvap as cvap, + con_cliq as cliq, + con_eps as eps, + con_epsm1 as epsm1, + con_e as e +) + + +def samfshalcnv_part1(data_dict): + """ + Scale-Aware Mass-Flux Shallow Convection + + :param data_dict: Dict of parameters required by the scheme + :type data_dict: Dict of either scalar or gt4py storage + """ + +############################ INITIALIZATION ############################ + + ### Input variables and arrays ### + im = data_dict["im"] + ix = data_dict["ix"] + km = data_dict["km"] + itc = data_dict["itc"] + ntc = data_dict["ntc"] + ntk = data_dict["ntk"] + ntr = data_dict["ntr"] + ncloud = data_dict["ncloud"] + clam = data_dict["clam"] + c0s = data_dict["c0s"] + c1 = data_dict["c1"] + asolfac = data_dict["asolfac"] + pgcon = data_dict["pgcon"] + delt = data_dict["delt"] + islimsk = data_dict["islimsk"] + psp = data_dict["psp"] + delp = data_dict["delp"] + prslp = data_dict["prslp"] + garea = data_dict["garea"] + hpbl = data_dict["hpbl"] + dot = data_dict["dot"] + phil = data_dict["phil"] + #fscav = data_dict["fscav"] + + ### Output buffers ### + kcnv = data_dict["kcnv"] + kbot = data_dict["kbot"] + ktop = data_dict["ktop"] + qtr = data_dict["qtr"] + q1 = data_dict["q1"] + t1 = data_dict["t1"] + u1 = data_dict["u1"] + v1 = data_dict["v1"] + rn = data_dict["rn"] + cnvw = data_dict["cnvw"] + cnvc = data_dict["cnvc"] + ud_mf = data_dict["ud_mf"] + dt_mf = data_dict["dt_mf"] + + shape = (1, ix, km) + + ### Local storages for 1D arrays (integer) ### + kpbl = gt.storage.ones (BACKEND, default_origin, shape, dtype=DTYPE_INT) + kb = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_INT) + kbcon = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_INT) + kbcon1 = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_INT) + ktcon = gt.storage.ones (BACKEND, default_origin, shape, dtype=DTYPE_INT) + ktcon1 = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_INT) + ktconn = gt.storage.ones (BACKEND, default_origin, shape, dtype=DTYPE_INT) + kbm = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_INT) + kmax = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_INT) + + ### Local storages for 1D arrays ("bool") ### + cnvflg = gt.storage.ones (BACKEND, default_origin, shape, dtype=DTYPE_INT) + flg = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_INT) + + ### Local storages for 1D arrays (float) ### + aa1 = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + cina = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + tkemean = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + clamt = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + ps = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + del0 = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + prsl = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + umean = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + tauadv = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + gdx = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + delhbar = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + delq = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + delq2 = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + delqbar = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + delqev = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + deltbar = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + deltv = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + dtconv = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + edt = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + pdot = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + po = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + qcond = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + qevap = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + hmax = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + rntot = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + vshear = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + xlamud = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + xmb = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + xmbmax = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + delubar = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + delvbar = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + c0 = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + wc = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + scaldfunc = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + sigmagfm = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + qlko_ktcon = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + sumx = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + tx1 = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + zi_ktcon1 = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + zi_kbcon1 = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + + ### Local storages for 2D arrays (float) ### + pfld = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + to = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + qo = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + uo = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + vo = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + qeso = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + wu2 = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + buo = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + drag = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + dellal = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + dbyo = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + zo = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + xlamue = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + heo = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + heso = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + dellah = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + dellaq = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + dellau = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + dellav = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + hcko = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + ucko = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + vcko = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + qcko = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + qrcko = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + eta = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + zi = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + pwo = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + c0t = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + cnvwt = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + + ### Local storages for 2D arrays (float, tracers), this will contain slices along n-axis ### + delebar = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + + ### Local storages for 3D arrays (float, tracers), this will contain slices along n-axis ### + ctr = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + ctro = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + dellae = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + ecko = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + qaero = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + + ### K-indices field ### + k_idx = gt.storage.from_array(np.indices(shape)[2] + 1, BACKEND, default_origin, dtype=DTYPE_INT) + + ### State buffer for 1D-2D interactions + state_buf1 = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_INT) + state_buf2 = gt.storage.zeros(BACKEND, default_origin, shape, dtype=DTYPE_INT) + + ### Local Parameters ### + g = grav + elocp = hvap/cp + el2orc = hvap * hvap/(rv * cp) + d0 = 0.001 + cm = 1.0 + delta = fv + fact1 = (cvap - cliq)/rv + fact2 = hvap/rv - fact1 * t0c + clamd = 0.1 + tkemx = 0.65 + tkemn = 0.05 + dtke = tkemx - tkemn + dthk = 25.0 + cinpcrmx = 180.0 + cinpcrmn = 120.0 + cinacrmx = -120.0 + cinacrmn = -80.0 + crtlamd = 3.0e-4 + dtmax = 10800.0 + dtmin = 600.0 + bet1 = 1.875 + cd1 = 0.506 + f1 = 2.0 + gam1 = 0.5 + betaw = 0.03 + dxcrt = 15.0e3 + h1 = 0.33333333 + tf = 233.16 + tcr = 263.16 + tcrf = 1.0/(tcr - tf) + + + ### Determine whether to perform aerosol transport ### + do_aerosols = (itc > 0) and (ntc > 0) and (ntr > 0) + if (do_aerosols): + do_aerosols = (ntr >= itc) + + ### Compute preliminary quantities needed for the static and feedback control portions of the algorithm ### + + # Convert input Pa terms to Cb terms + pa_to_cb( psp, prslp, delp, ps, prsl, del0) + + km1 = km - 1 + + ### Initialize storages (simple initializations already done above with gt4py functionalities) ### + + # Initialize column-integrated and other single-value-per-column + # variable arrays + init_col_arr( kcnv, cnvflg, kbot, ktop, + kbcon, kb, rn, gdx, garea, km=km) + + # Return to the calling routine if deep convection is present or the + # surface buoyancy flux is negative + if exit_routine(cnvflg, im): return + + # Initialize further parameters and arrays + init_par_and_arr( islimsk, c0, t1, c0t, cnvw, cnvc, ud_mf, dt_mf, + c0s=c0s, asolfac=asolfac, d0=d0) + + dt2 = delt + + # Model tunable parameters are all here + aafac = 0.05 + evfact = 0.3 + evfactl = 0.3 + w1l = -8.0e-3 + w2l = -4.0e-2 + w3l = -5.0e-3 + w4l = -5.0e-4 + w1s = -2.0e-4 + w2s = -2.0e-3 + w3s = -1.0e-3 + w4s = -2.0e-5 + + # Initialize the rest + init_kbm_kmax( kbm, k_idx, kmax, state_buf1, state_buf2, tx1, ps, prsl, km=km ) + init_final( kbm, k_idx, kmax, flg, cnvflg, kpbl, tx1, + ps, prsl, zo, phil, zi, pfld, eta, hcko, qcko, + qrcko, ucko, vcko, dbyo, pwo, dellal, to, qo, + uo, vo, wu2, buo, drag, cnvwt, qeso, heo, heso, hpbl, + t1, q1, u1, v1, km=km ) + + + # Tracers loop (THIS GOES AT THE END AND POSSIBLY MERGED WITH OTHER + # TRACER LOOPS!) --> better use version below + #for n in range(2, ntr+2): + + #kk = n-2 + + #qtr_shift = gt.storage.from_array(slice_to_3d(qtr[:, :, n]), BACKEND, default_origin) + + # Initialize tracers. Keep in mind that, qtr slice is for the + # n-th tracer, while the other storages are slices representing + # the (n-2)-th tracer. + #init_tracers( cnvflg, k_idx, kmax, ctr, ctro, ecko, qtr_shift ) + return {"heo":heo, "heso":heso, "qo":qo, "qeso":qeso, + "km":km, "kbm":kbm, "kmax":kmax, "kb":kb, "kpbl":kpbl, + "kbcon": kbcon, "ktcon":ktcon, #"ktcon1":ktcon1, "kbcon1":kbcon1 + "cnvflg":cnvflg, "tkemean":tkemean, "islimsk":islimsk, + "dot":dot, "aa1":aa1, "cina":cina, "clamt":clamt, "del":del0, + "pdot":pdot, "po":po, "hmax":hmax, "xlamud":xlamud, "pfld":pfld, + "to":to, "uo":uo, "vo":vo, "wu2":wu2, "buo":buo, "drag":drag, + "wc":wc, "dbyo":dbyo, "zo":zo, "xlamue":xlamue, "hcko":hcko, + "ucko":ucko, "vcko":vcko, "qcko":qcko, "eta":eta, "zi":zi, + "c0t":c0t, "sumx":sumx, "cnvwt":cnvwt, "dellal":dellal, + "ktconn":ktconn, "pwo":pwo, "qlko_ktcon":qlko_ktcon, "qrcko":qrcko, + "xmbmax":xmbmax, + "u1":u1, "v1":v1, "gdx":gdx, "garea":garea, "dtconv":dtconv, "delp":delp, + "cnvc":cnvc, "cnvw":cnvw, "t1":t1} + + +def test_part1_ser(): + data_dict = read_data(0, True, path = DATAPATH) + gt4py_dict = numpy_dict_to_gt4py_dict(data_dict) + out_dict_p2 = read_serialization_part2() + out_dict_p3 = read_serialization_part3() + out_dict_p4 = read_serialization_part4() + + ret_dict = samfshalcnv_part1(gt4py_dict) + exp_data = view_gt4pystorage(ret_dict) + + ref_data = view_gt4pystorage(out_dict_p2) + ref_data["u1"] = out_dict_p3["u1"].view(np.ndarray) + ref_data["v1"] = out_dict_p3["v1"].view(np.ndarray) + ref_data["gdx"] = out_dict_p3["gdx"].view(np.ndarray) + ref_data["garea"] = out_dict_p3["garea"].view(np.ndarray) + ref_data["dtconv"] = out_dict_p3["dtconv"].view(np.ndarray) + ref_data["delp"] = out_dict_p3["delp"].view(np.ndarray) + ref_data["cnvc"] = out_dict_p4["cnvc"].view(np.ndarray) + ref_data["cnvw"] = out_dict_p4["cnvw"].view(np.ndarray) + ref_data["t1"] = out_dict_p4["t1"].view(np.ndarray) + #ref_data = view_gt4pystorage({"heo":out_dict_p2["heo"], "heso":out_dict_p2["heso"], + # "qo":out_dict_p2["qo"], "qeso":out_dict_p2["qeso"], + # "km":out_dict_p2["km"], "kbm":out_dict_p2["kbm"], + # "kmax":out_dict_p2["kmax"], "kcnv":out_dict_p4["kcnv"], + # "cnvflg":out_dict_p2["cnvflg"]}) + + compare_data(exp_data, ref_data) + + +if __name__ == "__main__": + test_part1_ser() diff --git a/projects2020/group05/tests/test_part2.py b/projects2020/group05/tests/test_part2.py new file mode 100644 index 00000000..733772b0 --- /dev/null +++ b/projects2020/group05/tests/test_part2.py @@ -0,0 +1,529 @@ +import gt4py as gt +import sys +sys.path.append("..") +from read_serialization import * +from shalconv.kernels.utils import get_1D_from_index, exit_routine +from shalconv.kernels.stencils_part2 import * +from shalconv.serialization import * +from shalconv import * +from shalconv.physcons import ( + con_g as grav, + con_cp as cp, + con_hvap as hvap, + con_rv as rv, + con_fvirt as fv, + con_t0c as t0c, + con_rd as rd, + con_cvap as cvap, + con_cliq as cliq, + con_eps as eps, + con_epsm1 as epsm1, + con_e as e +) + + +def samfshalcnv_part2( ix,km,clam,pgcon,delt,c1,ncloud,ntk,ntr, + kpbl,kb,kbcon,kbcon1,ktcon,ktcon1,kbm,kmax, + po,to,qo,uo,vo,qeso,dbyo,zo, + heo,heso,hcko,ucko,vcko,qcko, + ecko,ecko_slice,ctro,ctro_slice, + aa1,cina,clamt,del0,wu2,buo,drag,wc, + pdot,hmax,xlamue,xlamud,pfld,qtr,tkemean, + eta,zi,c0t,sumx,cnvflg,flg,islimsk,dot, + k_idx,heo_kb,dot_kbcon,pfld_kbcon,pfld_kb,pfld_kbcon1, + cnvwt,dellal,ktconn,pwo,qlko_ktcon,qrcko,xmbmax ): + + ### Search in the PBL for the level of maximum moist static energy to start the ascending parcel. + stencil_static0( cnvflg, hmax, heo, kb, k_idx, kpbl, kmax, zo, to, qeso, qo, po, uo, vo, heso, pfld ) + + for n in range(ntr): + ctro_slice[...] = ctro[np.newaxis, :, :, n] + stencil_ntrstatic0( cnvflg, k_idx, kmax, ctro_slice ) + ctro[:, :, n] = ctro_slice[0, :, :] + + ### Search below the index "kbm" for the level of free convection (LFC) where the condition. + get_1D_from_index( heo, heo_kb, kb, k_idx ) + stencil_static1( cnvflg, flg, kbcon, kmax, k_idx, kbm, kb, heo_kb, heso ) + + ### If no LFC, return to the calling routine without modifying state variables. + if exit_routine(cnvflg, ix): return + + ### Determine the vertical pressure velocity at the LFC. + get_1D_from_index( dot, dot_kbcon, kbcon, k_idx ) + get_1D_from_index( pfld, pfld_kbcon, kbcon, k_idx ) + get_1D_from_index( pfld, pfld_kb, kb, k_idx ) + stencil_static2( cnvflg, pdot, dot_kbcon, islimsk, k_idx, kbcon, kb, pfld_kb, pfld_kbcon ) + + ### If no LFC, return to the calling routine without modifying state variables. + if exit_routine(cnvflg, ix): return + + ### turbulent entrainment rate assumed to be proportional to subcloud mean TKE + if(ntk > 0): + qtr_ntk = gt.storage.from_array( qtr[np.newaxis, :, :, ntk-1], BACKEND, default_origin ) + stencil_static3( sumx, tkemean, cnvflg, k_idx, kb, kbcon, zo, qtr_ntk, clamt, clam=clam ) + # qtr[:,:,ntr] = qtr_ntr[0,:,:] + #else: + #stencil_static4( cnvflg, clamt, clam=clam ) + + ### assume updraft entrainment rate is an inverse function of height + stencil_static5( cnvflg, xlamue, clamt, zi, xlamud, k_idx, kbcon, kb, + eta, ktconn, kmax, kbm, hcko, ucko, vcko, heo, uo, vo ) + + for n in range(ntr): + ctro_slice[...] = ctro[np.newaxis, :, :, n] + ecko_slice[...] = ecko[np.newaxis, :, :, n] + stencil_ntrstatic1( cnvflg, k_idx, kb, ecko_slice, ctro_slice ) + ecko[:, :, n] = ecko_slice[0, :, :] + + + stencil_static7( cnvflg, k_idx, kb, kmax, zi, xlamue, xlamud, hcko, heo, dbyo, + heso, ucko, uo, vcko, vo, pgcon=pgcon ) + + for n in range(ntr): + ctro_slice[...] = ctro[np.newaxis, :, :, n] + ecko_slice[...] = ecko[np.newaxis, :, :, n] + stencil_ntrstatic2( cnvflg, k_idx, kb, kmax, zi, xlamue, ecko_slice, ctro_slice ) + ecko[:, :, n] = ecko_slice[0, :, :] + + stencil_update_kbcon1_cnvflg( dbyo, cnvflg, kmax, kbm, kbcon, kbcon1, flg, k_idx ) + get_1D_from_index( pfld, pfld_kbcon1, kbcon1, k_idx ) + stencil_static9( cnvflg, pfld_kbcon, pfld_kbcon1 ) + + if exit_routine(cnvflg, ix): return + + ### calculate convective inhibition + stencil_static10( cina, cnvflg, k_idx, kb, kbcon1, zo, qeso, to, + dbyo, qo, pdot, islimsk ) + + if exit_routine(cnvflg, ix): return + + dt2 = delt + stencil_static11( flg, cnvflg, ktcon, kbm, kbcon1, dbyo, kbcon, del0, xmbmax, + aa1, kb, qcko, qo, qrcko, zi, qeso, to, xlamue, + xlamud, eta, c0t, dellal, buo, drag, zo, k_idx, pwo, + cnvwt, c1=c1, dt2=dt2, ncloud=ncloud ) + + if exit_routine(cnvflg, ix): return + + stencil_static12( cnvflg, aa1, flg, ktcon1, kbm, k_idx, ktcon, zo, qeso, + to, dbyo, zi, xlamue, xlamud, qcko, qrcko, qo, eta, del0, + c0t, pwo, cnvwt, buo, wu2, wc, sumx, kbcon1, drag, dellal, + c1=c1, ncloud=ncloud ) + + #if(ncloud > 0): + stencil_static13( cnvflg, k_idx, ktcon, qeso, to, dbyo, qcko, qlko_ktcon ) + # else: + # stencil_static14( cnvflg, vshear, k_idx, kb, ktcon, uo, vo, zi, edt ) + + +def apply_arguments_part2( input_dict, data_dict ): + + clam = input_dict['clam'] + pgcon = input_dict['pgcon'] + delt = input_dict['delt'] + c1 = input_dict['c1'] + ncloud = input_dict['ncloud'] + ntk = input_dict['ntk'] + ntr = input_dict['ntr'] + qtr = data_dict['qtr'] + tkemean = data_dict['tkemean'] + ix = data_dict['ix'] + km = data_dict['km'] + islimsk = data_dict['islimsk'] + dot = data_dict['dot'] + kpbl = data_dict['kpbl'] + kb = data_dict['kb'] + kbcon = data_dict['kbcon'] + kbcon1 = data_dict['kbcon1'] + ktcon = data_dict['ktcon'] + ktcon1 = data_dict['ktcon1'] + kbm = data_dict['kbm'] + kmax = data_dict['kmax'] + aa1 = data_dict['aa1'] + cina = data_dict['cina'] + clamt = data_dict['clamt'] + del0 = data_dict['del'] + pdot = data_dict['pdot'] + po = data_dict['po'] + hmax = data_dict['hmax'] + xlamud = data_dict['xlamud'] + pfld = data_dict['pfld'] + to = data_dict['to'] + qo = data_dict['qo'] + uo = data_dict['uo'] + vo = data_dict['vo'] + qeso = data_dict['qeso'] + wu2 = data_dict['wu2'] + buo = data_dict['buo'] + drag = data_dict['drag'] + wc = data_dict['wc'] + dbyo = data_dict['dbyo'] + zo = data_dict['zo'] + xlamue = data_dict['xlamue'] + heo = data_dict['heo'] + heso = data_dict['heso'] + hcko = data_dict['hcko'] + ucko = data_dict['ucko'] + vcko = data_dict['vcko'] + qcko = data_dict['qcko'] + ecko = data_dict['ecko'] + ctro = data_dict['ctro'] + eta = data_dict['eta'] + zi = data_dict['zi'] + c0t = data_dict['c0t'] + sumx = data_dict['sumx'] + cnvflg = data_dict['cnvflg'] + flg = data_dict['flg'] + cnvwt = data_dict['cnvwt'] + dellal = data_dict['dellal'] + ktconn = data_dict['ktconn'] + pwo = data_dict['pwo'] + qlko_ktcon = data_dict['qlko_ktcon'] + qrcko = data_dict['qrcko'] + xmbmax = data_dict['xmbmax'] + shape = (1, ix, km) + k_idx = gt.storage.from_array(np.indices(shape)[2] + 1, BACKEND, default_origin, dtype=DTYPE_INT) + heo_kb = gt.storage.empty(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + dot_kbcon = gt.storage.empty(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + pfld_kbcon = gt.storage.empty(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + pfld_kb = gt.storage.empty(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + pfld_kbcon1 = gt.storage.empty(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + ctro_slice = gt.storage.empty(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + ecko_slice = gt.storage.empty(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + + samfshalcnv_part2( ix, km, clam, pgcon, delt, c1, ncloud, ntk, ntr, + kpbl, kb, kbcon, kbcon1, ktcon, ktcon1, kbm, kmax, + po, to, qo, uo, vo, qeso, dbyo, zo, + heo, heso, hcko, ucko, vcko, qcko, + ecko, ecko_slice, ctro, ctro_slice, + aa1, cina, clamt, del0, wu2, buo, drag, wc, + pdot, hmax, xlamue, xlamud, pfld, qtr, tkemean, + eta, zi, c0t, sumx, cnvflg, flg, islimsk, dot, + k_idx, heo_kb, dot_kbcon, pfld_kbcon, pfld_kb, pfld_kbcon1, + cnvwt, dellal, ktconn, pwo, qlko_ktcon, qrcko, xmbmax ) + + return cnvflg, kmax, kbcon, kbcon1, cnvwt, dellal, ecko, ctro, pwo, qlko_ktcon, qrcko, xmbmax + + +def apply_arguments_stencil0(input_dict, data_dict): + cnvflg = data_dict['cnvflg'] + hmax = data_dict['hmax'] + heo = data_dict['heo'] + kb = data_dict['kb'] + kpbl = data_dict['kpbl'] + kmax = data_dict['kmax'] + zo = data_dict['zo'] + to = data_dict['to'] + qeso = data_dict['qeso'] + qo = data_dict['qo'] + po = data_dict['po'] + uo = data_dict['uo'] + vo = data_dict['vo'] + heso = data_dict['heso'] + pfld = data_dict['pfld'] + ix = input_dict['ix'] + km = input_dict['km'] + shape = (1, ix, km) + k_idx = gt.storage.from_array(np.indices(shape)[2] + 1, BACKEND, default_origin, dtype=DTYPE_INT) + + stencil_static0( cnvflg, hmax, heo, kb, k_idx, kpbl, kmax, zo, to, qeso, qo, po, uo, vo, heso, pfld ) + + return hmax, heo, heso, kb, to, qeso, qo, po, uo, vo + + +def apply_arguments_stencil012(input_dict, data_dict): + cnvflg = data_dict['cnvflg'] + hmax = data_dict['hmax'] + heo = data_dict['heo'] + kb = data_dict['kb'] + kpbl = data_dict['kpbl'] + kmax = data_dict['kmax'] + zo = data_dict['zo'] + to = data_dict['to'] + qeso = data_dict['qeso'] + qo = data_dict['qo'] + po = data_dict['po'] + uo = data_dict['uo'] + vo = data_dict['vo'] + heso = data_dict['heso'] + pfld = data_dict['pfld'] + flg = data_dict['flg'] + kbcon = data_dict['kbcon'] + kbm = data_dict['kbm'] + pdot = data_dict['pdot'] + dot = data_dict['dot'] + islimsk = data_dict['islimsk'] + ix = input_dict['ix'] + km = input_dict['km'] + + shape = (1, ix, km) + k_idx = gt.storage.from_array(np.indices(shape)[2] + 1, BACKEND, default_origin, dtype=DTYPE_INT) + heo_kb = gt.storage.empty(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + dot_kbcon = gt.storage.empty(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + pfld_kbcon = gt.storage.empty(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + pfld_kb = gt.storage.empty(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + + stencil_static0( cnvflg, hmax, heo, kb, k_idx, kpbl, kmax, zo, to, qeso, qo, po, uo, vo, heso, pfld ) + + get_1D_from_index( heo, heo_kb, kb, k_idx ) + + stencil_static1( cnvflg, flg, kbcon, kmax, k_idx, kbm, kb, heo_kb, heso ) + + if exit_routine(cnvflg, ix): return + + get_1D_from_index( dot, dot_kbcon, kbcon, k_idx ) + get_1D_from_index( pfld, pfld_kbcon, kbcon, k_idx ) + get_1D_from_index( pfld, pfld_kb, kb, k_idx ) + + stencil_static2( cnvflg, pdot, dot_kbcon, islimsk, k_idx, kbcon, kb, pfld_kb, pfld_kbcon ) + + return cnvflg, pdot + + +def apply_arguments_stencil012345(input_dict, data_dict): + clam = input_dict['clam'] + ntk = input_dict['ntk'] + qtr = data_dict['qtr'] + tkemean = data_dict['tkemean'] + sumx = data_dict['sumx'] + ix = data_dict['ix'] + km = data_dict['km'] + islimsk = data_dict['islimsk'] + dot = data_dict['dot'] + kpbl = data_dict['kpbl'] + kb = data_dict['kb'] + kbcon = data_dict['kbcon'] + kbm = data_dict['kbm'] + kmax = data_dict['kmax'] + clamt = data_dict['clamt'] + pdot = data_dict['pdot'] + po = data_dict['po'] + hmax = data_dict['hmax'] + xlamud = data_dict['xlamud'] + pfld = data_dict['pfld'] + to = data_dict['to'] + qo = data_dict['qo'] + uo = data_dict['uo'] + vo = data_dict['vo'] + qeso = data_dict['qeso'] + zo = data_dict['zo'] + xlamue = data_dict['xlamue'] + heo = data_dict['heo'] + heso = data_dict['heso'] + hcko = data_dict['hcko'] + ucko = data_dict['ucko'] + vcko = data_dict['vcko'] + eta = data_dict['eta'] + zi = data_dict['zi'] + cnvflg = data_dict['cnvflg'] + flg = data_dict['flg'] + ktconn = data_dict['ktconn'] + + shape = (1, ix, km) + k_idx = gt.storage.from_array(np.indices(shape)[2] + 1, BACKEND, default_origin, dtype=DTYPE_INT) + heo_kb = gt.storage.empty(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + dot_kbcon = gt.storage.empty(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + pfld_kbcon = gt.storage.empty(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + pfld_kb = gt.storage.empty(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + pfld_kbcon1 = gt.storage.empty(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + qtr_ntk = gt.storage.from_array(qtr[np.newaxis, :, :, ntk-1], BACKEND, default_origin) + + stencil_static0( cnvflg, hmax, heo, kb, k_idx, kpbl, kmax, zo, to, qeso, qo, po, uo, vo, heso, pfld ) + + get_1D_from_index( heo, heo_kb, kb, k_idx ) + + stencil_static1( cnvflg, flg, kbcon, kmax, k_idx, kbm, kb, heo_kb, heso ) + + get_1D_from_index( dot, dot_kbcon, kbcon, k_idx ) + get_1D_from_index( pfld, pfld_kbcon, kbcon, k_idx ) + get_1D_from_index( pfld, pfld_kb, kb, k_idx ) + + stencil_static2( cnvflg, pdot, dot_kbcon, islimsk, k_idx, kbcon, kb, pfld_kb, pfld_kbcon ) + stencil_static3( sumx, tkemean, cnvflg, k_idx, kb, kbcon, zo, qtr_ntk, clamt, clam ) + stencil_static5( cnvflg, xlamue, clamt, zi, xlamud, k_idx, kbcon, kb, + eta, ktconn, kmax, kbm, hcko, ucko, vcko, heo, uo, vo ) + + return clamt, xlamud, xlamue, eta, kmax, kbm, hcko, ucko, vcko, tkemean, sumx + + +def test_part2_1(): + input_dict = read_data(0, True, path=DATAPATH) + data_dict = read_serialization_part2() + out_dict = read_serialization_part2_1() + + hmax, heo, heso, kb, to, qeso, qo, po, uo, vo = apply_arguments_stencil0( input_dict, data_dict ) + + exp_data = view_gt4pystorage( {"hmax":hmax, "heo":heo, "heso": heso, "kb":kb, "to":to, + "qeso":qeso, "qo":qo, "po":po, "uo":uo, "vo":vo} ) + ref_data = view_gt4pystorage( {"hmax":out_dict["hmax"], "heo":out_dict["heo"], + "heso": out_dict["heso"], "kb":out_dict["kb"], + "to":out_dict["to"], "qeso":out_dict["qeso"], + "qo":out_dict["qo"], "po":out_dict["po"], + "uo":out_dict["uo"], "vo":out_dict["vo"]} ) + + compare_data(exp_data, ref_data) + + +def test_part2_2(): + input_dict = read_data(0, True, path=DATAPATH) + data_dict = read_serialization_part2() + out_dict = read_serialization_part2_2() + + cnvflg, pdot = apply_arguments_stencil012( input_dict, data_dict ) + + exp_data = view_gt4pystorage( {"cnvflg":cnvflg, "pdot": pdot} ) + ref_data = view_gt4pystorage( {"cnvflg":out_dict["cnvflg"],"pdot": out_dict["pdot"]} ) + + compare_data(exp_data, ref_data) + + +def test_part2_3(): + input_dict = read_data(0, True, path=DATAPATH) + data_dict = read_serialization_part2() + out_dict = read_serialization_part2_3() + + clamt, xlamud, xlamue, eta, kmax, kbm, hcko, ucko, vcko, tkemean, sumx = apply_arguments_stencil012345( input_dict, data_dict ) + + exp_data = view_gt4pystorage( {"clamt":clamt, "xlamud":xlamud, "xlamue": xlamue, "eta":eta, "kmax":kmax, + "kbm":kbm, "hcko":hcko, "ucko":ucko, "vcko":vcko, "tkemean":tkemean, + "sumx":sumx} ) + ref_data = view_gt4pystorage( {"clamt":out_dict["clamt"], "xlamud":out_dict["xlamud"], + "xlamue": out_dict["xlamue"], "eta":out_dict["eta"], + "kmax":out_dict["kmax"], "kbm":out_dict["kbm"], + "hcko":out_dict["hcko"], "ucko":out_dict["ucko"], + "vcko":out_dict["vcko"], "tkemean": out_dict["tkemean"], + "sumx":out_dict["sumx"]} ) + + compare_data(exp_data, ref_data) + + +def test_part2_4(): + data_dict = read_serialization_part2_3() + out_dict = read_serialization_part2_4() + + cnvflg = data_dict["cnvflg"] + kb = data_dict["kb"] + kmax = data_dict["kmax"] + zi = data_dict["zi"] + xlamue = data_dict["xlamue"] + xlamud = data_dict["xlamud"] + hcko = data_dict["hcko"] + heo = data_dict["heo"] + dbyo = data_dict["dbyo"] + heso = data_dict["heso"] + pgcon = data_dict["pgcon"] + ucko = data_dict["ucko"] + uo = data_dict["uo"] + vcko = data_dict["vcko"] + vo = data_dict["vo"] + ix = data_dict['ix'] + km = data_dict['km'] + + shape = (1, ix, km) + k_idx = gt.storage.from_array(np.indices(shape)[2] + 1, BACKEND, default_origin, dtype=DTYPE_INT) + + stencil_static7( cnvflg, k_idx, kb, kmax, zi, xlamue, xlamud, hcko, heo, dbyo, + heso, pgcon, ucko, uo, vcko, vo ) + + exp_data = view_gt4pystorage( {"hcko":hcko, "dbyo":dbyo, "ucko":ucko, "vcko":vcko} ) + ref_data = view_gt4pystorage( {"hcko": out_dict["hcko"], "dbyo": out_dict["dbyo"], + "ucko": out_dict["ucko"], "vcko": out_dict["vcko"]} ) + + compare_data(exp_data, ref_data) + + +def test_part2_5(): + data_dict = read_serialization_part2_4() + out_dict = read_serialization_part2_5() + + dbyo = data_dict["dbyo"] + cnvflg = data_dict["cnvflg"] + kmax = data_dict["kmax"] + kbm = data_dict["kbm"] + kbcon = data_dict["kbcon"] + kbcon1 = data_dict["kbcon1"] + flg = data_dict["flg"] + pfld = data_dict["pfld"] + ix = data_dict['ix'] + km = data_dict['km'] + + shape = (1, ix, km) + k_idx = gt.storage.from_array(np.indices(shape)[2] + 1, BACKEND, default_origin, dtype=DTYPE_INT) + pfld_kbcon = gt.storage.empty(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + pfld_kbcon1 = gt.storage.empty(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + + get_1D_from_index( pfld, pfld_kbcon, kbcon, k_idx ) + + stencil_update_kbcon1_cnvflg( dbyo, cnvflg, kmax, kbm, kbcon, kbcon1, flg, k_idx ) + + get_1D_from_index( pfld, pfld_kbcon1, kbcon1, k_idx ) + + stencil_static9( cnvflg, pfld_kbcon, pfld_kbcon1 ) + + exp_data = view_gt4pystorage( {"cnvflg":cnvflg, "kbcon1":kbcon1, "flg":flg} ) + ref_data = view_gt4pystorage( {"cnvflg": out_dict["cnvflg"], "kbcon1": out_dict["kbcon1"], + "flg": out_dict["flg"]} ) + + compare_data(exp_data, ref_data) + + +def test_part2_6(): + data_dict = read_serialization_part2_5() + out_dict = read_serialization_part2_6() + + cina = data_dict["cina"] + cnvflg = data_dict["cnvflg"] + kb = data_dict["kb"] + kbcon1 = data_dict["kbcon1"] + zo = data_dict["zo"] + qeso = data_dict["qeso"] + to = data_dict["to"] + dbyo = data_dict["dbyo"] + qo = data_dict["qo"] + pdot = data_dict["pdot"] + islimsk = data_dict["islimsk"] + ix = data_dict['ix'] + km = data_dict['km'] + + shape = (1, ix, km) + k_idx = gt.storage.from_array(np.indices(shape)[2] + 1, BACKEND, default_origin, dtype=DTYPE_INT) + + stencil_static10( cina, cnvflg, k_idx, kb, kbcon1, zo, qeso, to, + dbyo, qo, pdot, islimsk ) + + exp_data = view_gt4pystorage( {"cnvflg": cnvflg, "cina": cina} ) + ref_data = view_gt4pystorage( {"cnvflg": out_dict["cnvflg"], "cina": out_dict["cina"]} ) + + compare_data(exp_data, ref_data) + + +def test_part2(): + input_dict = read_data(0, True, path = DATAPATH) + data_dict = read_serialization_part2() + out_dict_p3 = read_serialization_part3() + out_dict_p4 = read_serialization_part4() + + cnvflg, kmax, kbcon, kbcon1, cnvwt, dellal, ecko, ctro, pwo, qlko_ktcon, qrcko, xmbmax = apply_arguments_part2( input_dict, data_dict ) + + exp_data = view_gt4pystorage( {"cnvflg":cnvflg, "ecko":ecko, "ctro":ctro, + "kmax":kmax, "kbcon": kbcon, "kbcon1":kbcon1, + "cnvwt":cnvwt, "dellal":dellal, "pwo":pwo, + "qlko_ktcon":qlko_ktcon, "qrcko":qrcko, "xmbmax":xmbmax} ) + ref_data = view_gt4pystorage( {"cnvflg":out_dict_p3["cnvflg"], "ctro":out_dict_p3["ctro"], + "ecko":out_dict_p3["ecko"],"kmax":out_dict_p3["kmax"], + "kbcon":out_dict_p3["kbcon"],"kbcon1":out_dict_p3["kbcon1"], + "cnvwt":out_dict_p4["cnvwt"], "dellal":out_dict_p3["dellal"], + "pwo": out_dict_p4["pwo"], "qlko_ktcon": out_dict_p3["qlko_ktcon"], + "qrcko":out_dict_p3["qrcko"],"xmbmax":out_dict_p3["xmbmax"]} ) + + compare_data(exp_data, ref_data) + + +if __name__ == "__main__": + #test_part2_1() + #test_part2_2() + #test_part2_3() + #test_part2_4() + #test_part2_5() + #test_part2_6() + test_part2() diff --git a/projects2020/group05/tests/test_part34.py b/projects2020/group05/tests/test_part34.py new file mode 100644 index 00000000..8423e35e --- /dev/null +++ b/projects2020/group05/tests/test_part34.py @@ -0,0 +1,227 @@ +import pytest +import gt4py as gt +from gt4py import gtscript +import sys +sys.path.append("..") +from read_serialization import read_serialization_part3, read_serialization_part4 +from shalconv.kernels.stencils_part34 import * +from shalconv.serialization import * +from shalconv import * +from shalconv.physcons import ( + con_g as grav, + con_cp as cp, + con_hvap as hvap, + con_rv as rv, + con_fvirt as fv, + con_t0c as t0c, + con_rd as rd, + con_cvap as cvap, + con_cliq as cliq, + con_eps as eps, + con_epsm1 as epsm1, + con_e as e +) + + +def samfshalcnv_part3(input_dict, data_dict): + """ + Scale-Aware Mass-Flux Shallow Convection + + :param data_dict: Dict of parameters required by the scheme + :type data_dict: Dict of either scalar or gt4py storage + """ + ix = input_dict["ix"] + km = input_dict["km"] + shape = (1, ix, km) + + g = grav + betaw = 0.03 + dtmin = 600.0 + dtmax = 10800.0 + dxcrt = 15.0e3 + + dt2 = input_dict["delt"] + cnvflg = data_dict["cnvflg"] + k_idx = gt.storage.from_array(np.indices(shape)[2] + 1, BACKEND, default_origin, dtype=DTYPE_INT) + kmax = data_dict["kmax"] + kb = data_dict["kb"] + ktcon = data_dict["ktcon"] + ktcon1 = data_dict["ktcon1"] + kbcon1 = data_dict["kbcon1"] + kbcon = data_dict["kbcon"] + dellah = data_dict["dellah"] + dellaq = data_dict["dellaq"] + dellau = data_dict["dellau"] + dellav = data_dict["dellav"] + del0 = data_dict["del"] + zi = data_dict["zi"] + zi_ktcon1 = gt.storage.empty(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + zi_kbcon1 = gt.storage.empty(BACKEND, default_origin, shape, dtype=DTYPE_FLOAT) + heo = data_dict["heo"] + qo = data_dict["qo"] + xlamue = data_dict["xlamue"] + xlamud = data_dict["xlamud"] + eta = data_dict["eta"] + hcko = data_dict["hcko"] + qrcko = data_dict["qrcko"] + uo = data_dict["uo"] + ucko = data_dict["ucko"] + vo = data_dict["vo"] + vcko = data_dict["vcko"] + qcko = data_dict["qcko"] + dellal = data_dict["dellal"] + qlko_ktcon = data_dict["qlko_ktcon"] + wc = data_dict["wc"] + gdx = data_dict["gdx"] + dtconv = data_dict["dtconv"] + u1 = data_dict["u1"] + v1 = data_dict["v1"] + po = data_dict["po"] + to = data_dict["to"] + tauadv = data_dict["tauadv"] + xmb = data_dict["xmb"] + sigmagfm = data_dict["sigmagfm"] + garea = data_dict["garea"] + scaldfunc = data_dict["scaldfunc"] + xmbmax = data_dict["xmbmax"] + sumx = data_dict["sumx"] + umean = data_dict["umean"] + + #import pdb; pdb.set_trace() + + # Calculate the tendencies of the state variables (per unit cloud base + # mass flux) and the cloud base mass flux + comp_tendencies( cnvflg, k_idx, kmax, kb, ktcon, ktcon1, kbcon1, kbcon, + dellah, dellaq, dellau, dellav, del0, zi, zi_ktcon1, + zi_kbcon1, heo, qo, xlamue, xlamud, eta, hcko, + qrcko, uo, ucko, vo, vcko, qcko, dellal, + qlko_ktcon, wc, gdx, dtconv, u1, v1, po, to, + tauadv, xmb, sigmagfm, garea, scaldfunc, xmbmax, + sumx, umean, + g=g, betaw=betaw, dtmin=dtmin, dt2=dt2, dtmax=dtmax, dxcrt=dxcrt ) + + return dellah, dellaq, dellau, dellav, dellal, xmb, sigmagfm + + +def samfshalcnv_part4(input_dict, data_dict): + """ + Scale-Aware Mass-Flux Shallow Convection + + :param data_dict: Dict of parameters required by the scheme + :type data_dict: Dict of either scalar or gt4py storage + """ + ix = input_dict["ix"] + km = input_dict["km"] + shape = (1, ix, km) + + g = grav + evfact = 0.3 + evfactl = 0.3 + elocp = hvap/cp + el2orc = hvap * hvap/(rv * cp) + + dt2 = input_dict["delt"] + cnvflg = data_dict["cnvflg"] + k_idx = gt.storage.from_array(np.indices(shape)[2] + 1, BACKEND, default_origin, dtype=DTYPE_INT) + kmax = data_dict["kmax"] + kb = data_dict["kb"] + ktcon = data_dict["ktcon"] + flg = data_dict["flg"] + islimsk = data_dict["islimsk"] + ktop = data_dict["ktop"] + kbot = data_dict["kbot"] + kcnv = data_dict["kcnv"] + kbcon = data_dict["kbcon"] + qeso = data_dict["qeso"] + pfld = data_dict["pfld"] + delhbar = data_dict["delhbar"] + delqbar = data_dict["delqbar"] + deltbar = data_dict["deltbar"] + delubar = data_dict["delubar"] + delvbar = data_dict["delvbar"] + qcond = data_dict["qcond"] + dellah = data_dict["dellah"] + dellaq = data_dict["dellaq"] + dellau = data_dict["dellau"] + dellav = data_dict["dellav"] + t1 = data_dict["t1"] + q1 = data_dict["q1"] + del0 = data_dict["del"] + rntot = data_dict["rntot"] + delqev = data_dict["delqev"] + delq2 = data_dict["delq2"] + pwo = data_dict["pwo"] + deltv = data_dict["deltv"] + delq = data_dict["delq"] + qevap = data_dict["qevap"] + rn = data_dict["rn"] + edt = data_dict["edt"] + cnvw = data_dict["cnvw"] + cnvwt = data_dict["cnvwt"] + cnvc = data_dict["cnvc"] + ud_mf = data_dict["ud_mf"] + dt_mf = data_dict["dt_mf"] + u1 = data_dict["u1"] + v1 = data_dict["v1"] + xmb = data_dict["xmb"] + eta = data_dict["eta"] + + #import pdb; pdb.set_trace() + + # For the "feedback control", calculate updated values of the state + # variables by multiplying the cloud base mass flux and the + # tendencies calculated per unit cloud base mass flux from the + # static control + feedback_control_update( cnvflg, k_idx, kmax, kb, ktcon, flg, + islimsk, ktop, kbot, kbcon, kcnv, qeso, + pfld, delhbar, delqbar, deltbar, delubar, + delvbar, qcond, dellah, dellaq, t1, xmb, + q1, u1, dellau, v1, dellav, del0, rntot, + delqev, delq2, pwo, deltv, delq, qevap, rn, + edt, cnvw, cnvwt, cnvc, ud_mf, dt_mf, eta, + dt2=dt2, g=g, evfact=evfact, evfactl=evfactl, + el2orc=el2orc, elocp=elocp ) + + return kcnv, kbot, ktop, q1, t1, u1, v1, rn, cnvw, cnvc, ud_mf, dt_mf + + +def test_part3(): + + input_dict = read_data(0, True, path = DATAPATH) + data_dict = read_serialization_part3() + out_dict = read_serialization_part4() + + dellah, dellaq, dellau, dellav, dellal, xmb, sigmagfm = samfshalcnv_part3( input_dict, data_dict ) + exp_data = view_gt4pystorage({"dellah":dellah,"dellaq":dellaq, + "dellau":dellau,"dellav":dellav, + "dellal":dellal,"xmb":xmb,"sigmagfm":sigmagfm}) + compare_data(exp_data, + {"dellah":out_dict["dellah"].view(np.ndarray),"dellaq":out_dict["dellaq"].view(np.ndarray), + "dellau":out_dict["dellau"].view(np.ndarray),"dellav":out_dict["dellav"].view(np.ndarray), + "dellal":out_dict["dellal"].view(np.ndarray),"xmb":out_dict["xmb"].view(np.ndarray),"sigmagfm":out_dict["sigmagfm"].view(np.ndarray)}) + + +def test_part4(): + + input_dict = read_data(0, True, path = DATAPATH) + data_dict = read_serialization_part4() + out_dict = read_data(0, False, path = DATAPATH) + + kcnv, kbot, ktop, q1, t1, u1, v1, rn, cnvw, cnvc, ud_mf, dt_mf = samfshalcnv_part4( input_dict, data_dict ) + exp_data = view_gt4pystorage( {"kcnv":kcnv[0,:,0],"kbot":kbot[0,:,0],"ktop":ktop[0,:,0], + "q1":q1[0,:,:],"t1":t1[0,:,:], + "u1":u1[0,:,:],"v1":v1[0,:,:],"rn":rn[0,:,0], + "cnvw":cnvw[0,:,:],"cnvc":cnvc[0,:,:],"ud_mf":ud_mf[0,:,:], + "dt_mf":dt_mf[0,:,:]} ) + + compare_data( exp_data, + {"kcnv":out_dict["kcnv"],"kbot":out_dict["kbot"],"ktop":out_dict["ktop"], + "q1":out_dict["q1"],"t1":out_dict["t1"], + "u1":out_dict["u1"],"v1":out_dict["v1"],"rn":out_dict["rn"], + "cnvw":out_dict["cnvw"],"cnvc":out_dict["cnvc"],"ud_mf":out_dict["ud_mf"], + "dt_mf":out_dict["dt_mf"]} ) + + +if __name__ == "__main__": + test_part3() + test_part4()