diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..d13458f0a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,65 @@ +--- +name: Bug report +about: Use this template for submitting bug reports. +title: "[ISSUE TITLE]" +labels: bug +assignees: +--- + + + + + +## Description + + +----- +**Screenshots** + + +----- +**Isca version** + + +----- +**System Information:** +To help us track down bugs faster, please provide the details about the machine where you run Isca. + +To do this, run the following code in Python: +```py +import platform +print(platform.uname()) +``` +and paste the output below. +```txt +# Paste system info here + +``` +----- +## Minimal reproducible example + diff --git a/.github/ISSUE_TEMPLATE/docs_issue.md b/.github/ISSUE_TEMPLATE/docs_issue.md new file mode 100644 index 000000000..c723af036 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/docs_issue.md @@ -0,0 +1,33 @@ +--- +name: Documentation issue +about: Use this template for submitting documentation-related issues. +title: "[ISSUE TITLE]" +labels: docs +assignees: +--- + + + + + +## Description + + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..80845a370 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,23 @@ +--- +name: Feature Request +about: Suggest an idea for something new to add +labels: +--- + +## Description + diff --git a/.github/ISSUE_TEMPLATE/installation_issue.md b/.github/ISSUE_TEMPLATE/installation_issue.md new file mode 100644 index 000000000..17456284d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/installation_issue.md @@ -0,0 +1,60 @@ +--- +name: Installation issue +about: Use this template if you have an issue related to installing Isca. +title: "[YOUR QUESTION]" +labels: infrastructure +assignees: +--- + + + + + +## Description + + +----- +**Screenshots** + + +----- +**Isca version** + + +----- +**System Information:** +To help us help you, provide the details about the machine where you are trying to install Isca. + +To do this, please run the following code in Python: +```py +import platform +print(platform.uname()) +``` +and paste the output below. +```txt +# Paste system info here + +``` diff --git a/.github/ISSUE_TEMPLATE/science_issue.md b/.github/ISSUE_TEMPLATE/science_issue.md new file mode 100644 index 000000000..c1980d82e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/science_issue.md @@ -0,0 +1,42 @@ +--- +name: Science issue +about: Use this template for submitting science-related issues. +title: "[YOUR QUESTION]" +labels: +assignees: +--- + + + + + +## Description + + +### Screenshots + + +## Minimal reproducible example + diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 000000000..94d4efcbe --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,85 @@ +name: docs + +on: + push: + branches: [ master ] + paths: + - '.github/workflows/docs.yml' + - 'docs/**' + - 'src/**' + +env: + GFDL_BASE: ${{ github.workspace }} + GFDL_ENV: "ubuntu_conda" + GFDL_WORK: "$HOME/gfdl_work" + GFDL_DATA: "$HOME/gfdl_data" + GFDL_PYDIR: "${{ github.workspace }}/src/extra/python" + +jobs: + docs: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest] + python-version: ['3.9'] + + steps: + - name: Checkout source + uses: actions/checkout@v2 + with: + fetch-depth: 0 + + # - name: Check environment variables + # run: | + # echo "Testing" + # echo $PWD + # echo $GFDL_BASE + # echo $GFDL_PYDIR + # echo $GITHUB_WORKSPACE + # echo $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID + + - name: Setup Conda Environment + uses: conda-incubator/setup-miniconda@v2 + with: + python-version: ${{ matrix.python-version }} + environment-file: docs/environment.yml + miniforge-variant: Mambaforge + miniforge-version: "latest" + use-mamba: true + activate-environment: isca_docs + auto-activate-base: false + + - name: List installed packages + shell: bash -l {0} + run: conda list + + - name: Install + shell: bash -l {0} + run: | + cd $GFDL_PYDIR + python -m pip install -e . + cd $GFDL_BASE + + - name: Build docs + shell: bash -l {0} + run: | + cd docs/ + make html -e + + - name: Commit documentation changes + run: | + git clone https://github.com/$GITHUB_REPOSITORY.git --branch gh-pages --single-branch gh-pages + cp -r docs/_build/html/* gh-pages/ + cd gh-pages + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + git add . + git commit -m "Update documentation" -a || true + # The above command will fail if no changes were present, so we ignore that. + + - name: Push changes + uses: ad-m/github-push-action@master + with: + branch: gh-pages + directory: gh-pages + github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 000000000..ee29f791d --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,70 @@ +name: tests + +on: + push: + branches: [ master ] + paths: + - '.github/workflows/tests.yml' + - 'bin/**' + - 'bin/**' + - 'ci/**' + - 'exp/**' + - 'input/**' + - 'src/**' + - 'test/**' + pull_request: + branches: [ master ] + paths: + - '.github/workflows/tests.yml' + - 'bin/**' + - 'ci/**' + - 'exp/**' + - 'input/**' + - 'src/**' + - 'test/**' + +env: + GFDL_BASE: ${{ github.workspace }} + GFDL_ENV: "ubuntu_conda" + GFDL_WORK: "${{ github.workspace }}/../gfdl_work" + GFDL_DATA: "${{ github.workspace }}/../gfdl_data" + GFDL_PYDIR: "${{ github.workspace }}/src/extra/python" + +jobs: + test: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest] # , macos-latest] + python-version: ['3.12'] + + steps: + - name: Checkout source + uses: actions/checkout@v2 + + - name: Setup Conda for OS ${{ matrix.os }} and Python ${{ matrix.python-version }} + uses: conda-incubator/setup-miniconda@v2 + with: + python-version: ${{ matrix.python-version }} + environment-file: ci/environment-py${{ matrix.python-version }}_frozen.yml + miniforge-variant: Mambaforge + miniforge-version: "latest" + use-mamba: true + activate-environment: isca_env + auto-activate-base: false + + - name: Install + shell: bash -l {0} + run: | + cd $GFDL_PYDIR + python -m pip install -e . + cd $GFDL_BASE + + - name: List installed packages + shell: bash -l {0} + run: conda list + + - name: Run tests + shell: bash -l {0} + run: | + python -m pytest diff --git a/.gitignore b/.gitignore index 4333c2fac..3b7c89055 100644 --- a/.gitignore +++ b/.gitignore @@ -1,31 +1,115 @@ -exec.*/ +# Project-specific compilation*/ -*.x -*.o -*~ -~* -spin_up_restart/ -nohup.out -Makefile -old_exps -opt_exps -frierson_comparison_*old* -*.pyc +docs/source/examples/index.rst +exec.*/ exp/st_test* exp/archived/* -*.DS_Store -*.egg-info -._* -*.mod +frierson_comparison_*old* +*land_world_mountains.nc +mima_pz.txt +*output.txt +old_exps +opt_exps postprocessing/plevel_interpolation/scripts/*.nc postprocessing/plevel_interpolation/scripts/*.nml postprocessing/plevel_interpolation/scripts/archived/* -*land_world_mountains.nc -*output.txt +spin_up_restart/ src/extra/python/scripts/*.nc src/extra/python/scripts/archived/ -mima_pz.txt *.sh.* src/atmos_param/socrates/src/trunk* + +# Temporary files +*.x +*.o +*~ +~* +nohup.out +Makefile +!docs/Makefile +*.DS_Store +._* +*.mod + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +pip-wheel-metadata/ + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ test/.cache test/results.xml +*.sh.e* +*.sh.o* + +# Translations +*.mo +*.pot + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter +Untitled*.ipynb +.ipynb_checkpoints + +# pyenv +.python-version + +# dotenv +.env + +# virtualenv +.venv +venv/ +ENV/ + +# Spyder project settings +.spyderproject +.spyproject + +# mkdocs documentation +/site diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 000000000..27dc52038 --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,38 @@ +cff-version: 1.1.0 +authors: + - family-names: Vallis + given-names: Geoffrey K. + orcid: https://orcid.org/0000-0002-5971-8995 + - family-names: Colyer + given-names: Greg + - family-names: Geen + given-names: Ruth + - family-names: Gerber + given-names: Edwin + orcid: https://orcid.org/0000-0002-6010-6638 + - family-names: Jucker + given-names: Martin + orcid: https://orcid.org/0000-0002-4227-315X + - family-names: Maher + given-names: Penelope + orcid: https://orcid.org/0000-0001-8513-8700 + - family-names: Paterson + given-names: Alexander + - family-names: Pietschnig + given-names: Marianne + orcid: https://orcid.org/0000-0002-7405-5536 + - family-names: Penn + given-names: James + - family-names: Thomson + given-names: Stephen I. + orcid: https://orcid.org/0000-0002-4775-3259 +title: "Isca, v1.0: a framework for the global modelling of the atmospheres of Earth and other planets at varying levels of complexity" +journal: Geoscientific Model Development +volume: 11 +year: 2018 +number: 3 +pages: 843--859 +version: v1.0 +doi: 10.5194/gmd-11-843-2018 +url: https://gmd.copernicus.org/articles/11/843/2018/ +date-released: 2018-03-06 diff --git a/ReadMe.md b/ReadMe.md index c071cbd0e..46dbc6359 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -1,4 +1,31 @@ -# Isca +

+ + Isca
+

+ +

+ +Python 3.7+ + +Python 3.7+ + +black + +Tests + +Documentation + +License: GPL v3 + +twitter IscaModel +

Isca is a framework for the idealized modelling of the global circulation of planetary atmospheres at varying levels of complexity and realism. The @@ -25,11 +52,22 @@ Python scripts are also used to run the model on different architectures, to archive the output, and for diagnostics, graphics, and post-processing. All of these features are publicly available on a Git-based repository. -# Getting Started +## Getting Started A python module `isca` (note lowercase) is provided alongside the Fortran source code that should help to do a lot of the heavy-lifting of compiling, configuring and running the model for you. Isca can be compiled, run and configured without using python, but using the python wrapper is recommended. -## Installing the `isca` python module +### Installing the `isca` python module + +The python module is found in the `src` directory and can be installed using `pip`. It's recommended that you use some sort of python environment manager to do this, such as using a conda distribution and creating an environment (in the code below called "`isca_env`"), or using `virtualenv` instead. This "getting started" will show you how to create a python environment that includes Isca's required packages, and then install the model. + +1. **Install [Miniforge](https://github.com/conda-forge/miniforge)** + +*Recommended step*: Some workstations may have outdated default python and conda installations, which may cause conflicts during installation. As a lightweight solution to get up-to-date installations, we recommend downloading [Miniforge](https://github.com/conda-forge/miniforge). +To ensure this works as expected, check that `$PYTHONPATH` is unset and that your `.bashrc` does not contain `module load` statements that may cause conda conflicts. + +*If you have a recent conda version installed in your home directory already you may wish to skip this step.* + +2. **Check out or download this repository** To begin you'll need a copy of the source code. Either fork the Isca repository to your own github username, or clone directly from the ExeClim group. @@ -38,19 +76,21 @@ $ git clone https://github.com/ExeClim/Isca $ cd Isca ``` -The python module is found in the `src` directory and can be installed using `pip`. It's recommended (but not essential) that you use some sort of python environment manager to do this, such as using the Anaconda distribution and creating an environment (in the code below called "`isca_env`"), or using `virtualenv` instead. This getting started will use Anaconda. +3. **Create a conda environment** +Requirements for Isca can be installed via the .yml file included with the model in `Isca/ci/environment-py3.12_frozen.yml` +Navigate to the downloaded Isca folder, and create a conda environment `isca_env` containing the required packages using: ```{bash} -$ conda create -n isca_env python ipython -... -$ source activate isca_env -(isca_env)$ cd src/extra/python -(isca_env)$ pip install -r requirements.txt -... -Successfully installed MarkupSafe-1.0 f90nml jinja2-2.9.6 numpy-1.13.3 pandas-0.21.0 python-dateutil-2.6.1 pytz-2017.3 sh-1.12.14 six-1.11.0 xarray-0.9.6 +$ conda env create -f ci/environment-py3.12_frozen.yml +``` +Then activate the environment; you'll need to do this each time you launch a new bash session. +```{bash} +$ conda activate isca_env ``` -Now install the `isca` python module in "development mode". This will allow you, if you wish, to edit the `src/extra/python/isca` files and have those changes be used when you next run an Isca script. +4. **Install the model** + +Now install the `isca` python module in "development mode". This will allow you, if you wish, to edit the `src/extra/python/isca` files and have those changes be used when you next run an Isca script. Navigate to `Isca/src/extra/python/` and run: ```{bash} (isca_env)$ pip install -e . @@ -58,35 +98,37 @@ Now install the `isca` python module in "development mode". This will allow you Successfully installed Isca ``` -## Compiling for the first time - -At Exeter University, Isca is compiled using: - -* Intel Compiler Suite 14.0 -* OpenMPI 10.0.1 -* NetCDF 4.3.3.1 -* git 2.1.2 +### Compiling for the first time -Different workstations/servers at different institutions will have different compilers and libraries available. The Isca framework assumes you have something similar to our stack at Exeter, but provides a hook for you to configure the environment in which the model is run. +By installing Isca using the `ci/environment-py3.12_frozen.yml` file, you will have installed everything you need to run Isca, including the gfortran compiler, openmpi and netcdf. Whilst this may not be the optimal way of running Isca on your machine, it should work without too much extra effort. -Before Isca is compiled/run, an environment is first configured which loads the specific compilers and libraries necessary to build the code. This done by setting the environment variable `GFDL_ENV` in your session. +Before Isca is compiled/run, an environment is first configured which loads the specific compilers and libraries necessary to build the code. This done by setting the environment variable `GFDL_ENV` in your session. There is an option within Isca to use set `GFDL_ENV=ubuntu_conda`, which is setup to use the gfortran compiler you installed via the environment file. -For example, on the EMPS workstations at Exeter, I have the following in my `~/.bashrc`: +To make use of this environment, you should add a version of the following to your `~/.bashrc`: ```{bash} # directory of the Isca source code export GFDL_BASE=/scratch/jamesp/Isca -# "environment" configuration for emps-gv4 -export GFDL_ENV=emps-gv +# "environment" configuration for use with ubuntu-conda +export GFDL_ENV=ubuntu_conda # temporary working directory used in running the model -export GFDL_WORK=/scratch/jamesp/gfdl_work +export GFDL_WORK=/scratch/jamesp/isca_work # directory for storing model output -export GFDL_DATA=/scratch/jamesp/gfdl_data +export GFDL_DATA=/scratch/jamesp/isca_data ``` -The value of `GFDL_ENV` corresponds to a file in `src/extra/env` that is sourced before each run or compilation. For an example that you could adapt to work on your machine, see `src/extra/env/emps-gv`. +The value of `GFDL_ENV` corresponds to a file in `src/extra/env` that is sourced before each run or compilation. + +You may wish to configure your own way to run Isca using locally available compilers, e.g. the Intel compilers. An example using such a setup is available here - `src/extra/env/emps-gv`. + +At Exeter University, Isca is compiled using: + +* Intel Compiler Suite 14.0 +* OpenMPI 10.0.1 +* NetCDF 4.3.3.1 +* git 2.1.2 -We are not able to provide support in configuring your environment at other institutions other than Exeter University - we suggest that you contact your friendly local sysops technician for guidance in getting the compilers and libraries collated if you are not sure how to proceed. +Different workstations/servers at different institutions will have different compilers and libraries available. The Isca framework should run 'out of the box' using the gfortran compilers installed from the environment file. However, if you want to install the model using your own configuration then we are not able to provide support in configuring your environment. We suggest that you contact your friendly local sysops technician for guidance in getting the compilers and libraries collated if you are not sure how to proceed. If you work at another large institution and have successfully compiled and run Isca, we welcome you to commit your own environment config to `/src/extra/env/my-new-env` for future scientists to benefit from and avoid the pain of debugging compilation! @@ -111,9 +153,23 @@ Once you've got an environment file that works for your machine saved in `src/ex There are some site-specific guides to running Isca on your local system located in the directory [exp/site_specific/](https://github.com/ExeClim/Isca/tree/master/exp/site_specific). -# License +## Contributing to Isca + +If you have made changes that you think will be useful to others, please feel free to suggest these as a Github pull request. +These might include adding site specific configurations that could be useful to future users, basic bug fixes, or addition of new options or modules for modeling your planet of choice. +An Isca team member will then review your Pull Request and suggest any changes needed before merging it in. Things to consider: +- Before submitting a pull request, double check that the branch to be merged contains only changes you wish to add to the master branch. This will save time in reviewing the code. +- If you add a new feature to the Fortran code, please make it off by default so that other users' results won't change if they update from the master. +- For any changes to model Fortran files, please run the trip-tests found in [/Isca/exp/test_cases/trip_test/](https://github.com/ExeClim/Isca/tree/master/exp/test_cases/trip_test). These compile and perform brief runs of some standard configurations to help identify any accidental changes to the model your commits may have caused. Isca includes a broad range of options, so try to take into consideration whether the changes you make will affect other configurations while you implement them. +- For substantial additions of code, for example an entirely new module, please also include a test case in [/Isca/exp/test_cases/](https://github.com/ExeClim/Isca/tree/master/exp/test_cases/). This helps in testing that the option works as expected, and provides support for future users in using the new configuration. +- Please do not make changes to existing test cases, these are here for trip-testing as well as user guidance. +- As well as our model work on Isca, we are all Isca users ourselves, so responding to Pull Requests may take time, but we will aim to respond to urgent queries as soon as we can. + +For more information, please read the [contributing guide](https://github.com/execlim/Isca/blob/master/docs/source/contributing.rst). + +## License -Isca is distributed under a GNU GPLv3 license. See the `LICENSE` file for details. +Isca is distributed under a GNU GPLv3 license. See the [`LICENSE`](LICENSE) file for details. RRTM/RRTMG: Copyright © 2002-2010, Atmospheric and Environmental Research, Inc. (AER, Inc.). This software may be used, copied, or redistributed as long as it is not sold and this @@ -122,7 +178,7 @@ any express or implied warranties. Some of the code provided in the `src/atmos_params/socrates/interface` folder were provided by the UK Met Office, and are therefore covered by British Crown Copyright. The copyright statement at the top of the -relevant code is provided below. For the `copyright.txt.` refered to in this statement, please see the +relevant code is provided below. For the `copyright.txt` refered to in this statement, please see the Socrates source code itself, which is downloadable from the Met Office, and is not packaged with Isca. ``` diff --git a/ci/environment-py.yml b/ci/environment-py.yml new file mode 100644 index 000000000..d9bc44bc6 --- /dev/null +++ b/ci/environment-py.yml @@ -0,0 +1,20 @@ +name: isca_env +channels: +- conda-forge +dependencies: +- dask +- f90nml +- fortran-compiler +- ipykernel +- jinja2 +- libgfortran +- netcdf-fortran +- numpy +- openmpi=4.* +- pandas +- python +- pip +- pytest +- sh +- tqdm +- xarray diff --git a/ci/environment-py3.12_frozen.yml b/ci/environment-py3.12_frozen.yml new file mode 100644 index 000000000..1caa81d60 --- /dev/null +++ b/ci/environment-py3.12_frozen.yml @@ -0,0 +1,20 @@ +name: isca_env +channels: +- conda-forge +dependencies: +- dask=2024.1.0 +- f90nml=1.4.4 +- fortran-compiler=1.7.0 +- ipykernel=6.29.0 +- jinja2=3.1.3 +- libgfortran=3.0.0 +- netcdf-fortran=4.6.1 +- numpy=1.26.3 +- openmpi=4.1.6 +- pandas=2.2.0 +- python=3.12.1 +- pip=23.3.2 +- pytest=7.4.4 +- sh=2.0.6 +- tqdm=4.66.1 +- xarray=2024.1.1 diff --git a/deprecated/.travis.yml b/deprecated/.travis.yml new file mode 100644 index 000000000..87ae63f57 --- /dev/null +++ b/deprecated/.travis.yml @@ -0,0 +1,60 @@ +language: minimal +notifications: + email: false + +branches: + only: + - master + +env: + global: + - GFDL_BASE=$TRAVIS_BUILD_DIR + - GFDL_ENV="ubuntu_conda" + - GFDL_WORK="$HOME/gfdl_work" + - GFDL_DATA="$HOME/gfdl_data" + - GFDL_PYDIR="$TRAVIS_BUILD_DIR/src/extra/python" + - GH_PAGES_DIR="$HOME/gh-pages" + +matrix: + fast_finish: true + include: + # - env: CONDA_ENV=docs + - env: CONDA_ENV=py37 + +before_install: + - wget http://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh; + - bash miniconda.sh -b -p $HOME/miniconda + - export PATH="$HOME/miniconda/bin:$PATH" + - hash -r + - conda config --set always_yes yes --set changeps1 no --set show_channel_urls true + - conda config --add channels conda-forge + - conda update -q conda + - conda info -a + +install: + - if [[ "$CONDA_ENV" == "docs" ]]; then + conda env create -n test_env --file docs/environment.yml; + else + conda env create -n test_env --file ci/environment_$CONDA_ENV.yml; + fi; + - source $TRAVIS_BUILD_DIR/src/extra/env/$GFDL_ENV + - source $HOME/miniconda/etc/profile.d/conda.sh + - conda activate test_env + - conda list + - cd $GFDL_PYDIR + - pip install -e . + - cd $GFDL_BASE + +script: + - if [[ "$CONDA_ENV" == "docs" ]]; then + sphinx-build -n -b html -d docs/_build/doctrees docs/source docs/_build/html; + else + travis_wait 30 pytest -v; + fi; + +after_success: + - if [[ "$CONDA_ENV" == "docs" ]]; then + bash ci/deploy_docs.sh; + else + echo "Here be conda upload command"; + fi; diff --git a/Jenkinsfile.in_prep b/deprecated/Jenkinsfile.in_prep similarity index 100% rename from Jenkinsfile.in_prep rename to deprecated/Jenkinsfile.in_prep diff --git a/deprecated/README.md b/deprecated/README.md new file mode 100644 index 000000000..daf28977a --- /dev/null +++ b/deprecated/README.md @@ -0,0 +1 @@ +Files in this directory will be deleted in the next release. diff --git a/deprecated/deploy_docs.sh b/deprecated/deploy_docs.sh new file mode 100644 index 000000000..ee882ddc7 --- /dev/null +++ b/deprecated/deploy_docs.sh @@ -0,0 +1,50 @@ +#!/bin/bash +# exit with nonzero exit code if anything fails +set -e + +# Clone *this* git repo, but only the gh-pages branch. +echo Cloning gh-pages... +if [[ ! -d $GH_PAGES_DIR ]]; then + git clone -q -b gh-pages --single-branch https://${GITHUB_API_KEY}@github.com/${TRAVIS_REPO_SLUG}.git $GH_PAGES_DIR +fi +cd $GH_PAGES_DIR + +# inside this git repo we pretend to be isca +git config user.name "isca-model" +git config user.email "iscagcm@gmail.com" + +# if [[ "${TRAVIS_TAG}" != "" ]]; then +# # export VERSION=${TRAVIS_TAG%.*} +# export VERSION=${TRAVIS_TAG} +# else +export VERSION="dev" +# fi + +# The first and only commit to this new Git repo contains all the +# files present with the commit message "Deploy to GitHub Pages". +echo Updating $VERSION docs... +rm -rf ${VERSION} +mkdir -p ${VERSION} +cp -R ${TRAVIS_BUILD_DIR}/docs/_build/html ${VERSION}/ +cp -R ${TRAVIS_BUILD_DIR}/docs/_build/doctrees ${VERSION}/ +touch .nojekyll +# if [[ "${VERSION}" != "dev" ]]; then +ln -snf ${VERSION} latest +# fi + +# Generate our json list of versions +# echo Generating versions.json... +# ${TRAVIS_BUILD_DIR}/ci/gen_versions_json.py + +echo Staging... +git add -A . +# if [[ "${VERSION}" == "dev" ]]; then +## && `git log -1 --format='%s'` == *"dev"* ]]; then +git commit --amend --reset-author --no-edit +# else +# git commit -m "Deploy ${VERSION} to GitHub Pages" +# fi + +# Push up to gh-pages +echo Pushing... +git push --force -q https://${GITHUB_API_KEY}@github.com/${TRAVIS_REPO_SLUG}.git gh-pages diff --git a/doc/.directory b/doc/.directory deleted file mode 100644 index f703455f3..000000000 --- a/doc/.directory +++ /dev/null @@ -1,3 +0,0 @@ -[Dolphin] -Timestamp=2015,10,4,8,21,9 -ViewMode=1 diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 000000000..00f45750b --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,19 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = -d _build/doctrees +SPHINXBUILD = sphinx-build +SOURCEDIR = source +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/environment.yml b/docs/environment.yml new file mode 100644 index 000000000..da8cdbe0e --- /dev/null +++ b/docs/environment.yml @@ -0,0 +1,23 @@ +name: isca_docs +channels: +- conda-forge +dependencies: +- dask +- f90nml +- fortran-compiler +- ipykernel +- jinja2 +- libgfortran +- netcdf-fortran +- nbsphinx +- numpy +- openmpi +- pandas +- python=3.9 +- pip +- pytest +- sh +- sphinx +- sphinx_rtd_theme +- tqdm +- xarray diff --git a/FieldNames.md b/docs/old/FieldNames.md similarity index 100% rename from FieldNames.md rename to docs/old/FieldNames.md diff --git a/Moist_model_code_structure_FMS2013.xlsx b/docs/old/Moist_model_code_structure_FMS2013.xlsx similarity index 100% rename from Moist_model_code_structure_FMS2013.xlsx rename to docs/old/Moist_model_code_structure_FMS2013.xlsx diff --git a/PhysOptions.md b/docs/old/PhysOptions.md similarity index 100% rename from PhysOptions.md rename to docs/old/PhysOptions.md diff --git a/doc/diag_table.html b/docs/old/diag_table.html similarity index 100% rename from doc/diag_table.html rename to docs/old/diag_table.html diff --git a/doc/field_manager.html b/docs/old/field_manager.html similarity index 100% rename from doc/field_manager.html rename to docs/old/field_manager.html diff --git a/ReadMe.orig b/docs/old/fms_readme.md similarity index 100% rename from ReadMe.orig rename to docs/old/fms_readme.md diff --git a/doc/mkmf.html b/docs/old/mkmf.html similarity index 100% rename from doc/mkmf.html rename to docs/old/mkmf.html diff --git a/doc/quickstart.html b/docs/old/quickstart.html similarity index 100% rename from doc/quickstart.html rename to docs/old/quickstart.html diff --git a/doc/quickstart.txt b/docs/old/quickstart.txt similarity index 100% rename from doc/quickstart.txt rename to docs/old/quickstart.txt diff --git a/doc/spectral_core.pdf b/docs/old/spectral_core.pdf similarity index 100% rename from doc/spectral_core.pdf rename to docs/old/spectral_core.pdf diff --git a/docs/source/_static/favicon.ico b/docs/source/_static/favicon.ico new file mode 100644 index 000000000..c627a7f76 Binary files /dev/null and b/docs/source/_static/favicon.ico differ diff --git a/docs/source/_static/isca_logo_large.png b/docs/source/_static/isca_logo_large.png new file mode 100644 index 000000000..601df2d24 Binary files /dev/null and b/docs/source/_static/isca_logo_large.png differ diff --git a/docs/source/_template.rst b/docs/source/_template.rst new file mode 100644 index 000000000..f91fffb12 --- /dev/null +++ b/docs/source/_template.rst @@ -0,0 +1,44 @@ +.. DO NOT MODIFY THIS FILE UNLESS YOU ARE A CORE MAINTAINER OF ISCA! + +.. + This is a reStructuredText template file for creating + a new documentation entry for the Isca model. + + Please make a copy of this file with the appropriate file name and place it + to the appropriate location within docs/source/ and start writing. + Once you are done, remove all the comments from your .rst file. + + Here is a guide on reST formatting: + https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html + +Template title +============== +.. Don't forget to add a concise and informative title. + +Summary +------- +.. Add a short abstract on what the relevant part of code does. + +Namelist options +---------------- + + +Diagnostics +----------- +.. What diagnostics are available for this part of the code. + + +Relevant modules and subroutines +-------------------------------- +.. List the names of relevant modules, subroutines, functions, etc. +.. You can add also code snippets, using Sphinx code formatting + + +References +---------- +.. + Add relevant references. This is done in 2 steps: + 1. Add the reference itself to docs/source/references.rst + 2. Insert the citation key here, e.g. [Vallis2017]_ + + See the Contributing guide for more info. diff --git a/docs/source/begginers_guide.rst b/docs/source/begginers_guide.rst new file mode 100644 index 000000000..2752d2813 --- /dev/null +++ b/docs/source/begginers_guide.rst @@ -0,0 +1,175 @@ +Isca Beginner's Guide +==================== + +Below is a list of reading and activities that will help you get comfortable using Isca. Be assured that your supervisor/tutor will not expect you to be fluent with this when you start using Isca, but it will help to have an idea of what to expect when you start running the model. + +This document is essentially a suggestion of signposts. With this kind of work, self-study and initiative is very important. It is up to you to go and research the topics until you feel comfortable. + +Note: users who are more familiar with scientific computing may find this document a little longwinded and may be better off looking at the `ReadMe `_ or this `guide on how to run Isca experiments `_. + +Essentials +------------- + +Users who will be using Isca to run simple planetary models under close guidance of a supervisor/teacher should learn about the following topics. Do not worry if it doesn’t make sense right now, you will understand more as you go on. + +ssh/terminals +^^^^^^^^^^^^^ +To run Isca you will need to be using quite a powerful computer, most laptops will not suffice, especially for high resolution runs. You’ll likely run Isca on a university owned *workstation* or *supercomputer*. Your supervisor will inform you of the name of the computer. However, these computers are *remote* – that is you do not sit in front of them and login to them as you might be used to. You have to login to them from another computer, e.g. your personal laptop. This is called *SSH*, or *Secure Shell*, a protocol that enables two computers to communicate and share data. + +To do this you will need a *terminal* on your own personal computer. Mac and linux computers will have a factory installed app for this called *terminal*. Windows users will need to download a piece of software, the most common is `PuTTY `_. These are *ssh clients*. This software is text based - everything is done using text commands from the *command line*, we talk about this more later. + +To login to the *unix server*, all you need to do is open the *terminal* and run something like: + +``ssh USER@computername.ex.ac.uk`` + +Your supervisor will tell you the precise command. + +You may also need to be connected to a *VPN (Virtual Private Network)*. For Exeter, if you are not on campus you will certainly need to use `this `_. + +There are some interesting videos on youtube on how the SSH protocol works: e.g. `here `_, but you don’t need to understand how it works in order to use it. + +Unix Servers +^^^^^^^^^^^^ +Most scientific computing is done on computers/servers that use an *unix* operating system. These do not have a *graphical user interface (GUI)*, everything is done with text commands. There are good guides on the internet, like `here `_. + +If you are an apple/linux user, you can just open the terminal app on your laptop and practise there. If you use windows, it maybe easiest to use this `emulator `_, then select the drop down arrow to the right of `New` and select `_Terminal.term`. Note because you’re using an emulator it may not be possible to follow the steps of the guide exactly, but try and get a feel of the commands they suggest (`cd, cd .., ls, mkdir, rm, pwd, mv, cp, less`). + +Python (namelists) +^^^^^^^^^^^^^^^^^^ +Isca is configured using *python* scripts, although the actual model is coded in *FORTRAN* (see :ref:`FORTRAN` in Advanced). `Python `_ is a powerful high-level programming language, similar to Matlab – but far better. + +At this stage you don’t need to be able to code in python, as Isca has prewritten scripts called *test cases* which you can just edit in order to change the model set up. Editing will require changing one or more *namelists*. The namelists are actually part of the FORTRAN code but we use *python packages* to pass these variables between the python script and the FORTRAN model. + +For example say that in the model the value for the CO2 concentration was 300, and you wanted to make it 600, all you would need to do is change: + +``'co2_conc' : 300.`` to ``'co2_conc' : 600.`` in your text editor (see :ref:`below`). + +If you haven’t used python before and want to become more familiar, there are hundreds of tutorials (e.g. `here `_) and videos. To practise you can use a *python notebook* on something like `Colab `_, or download software like `Anaconda `_. Python is comprised of the basic python *packages* and then additional *libraries* you have to install and *import*. In the future it may be useful to have python *environments* (see :ref:`Conda` in Intermediate). + +Text Editors +^^^^^^^^^^^^ +You will need to be able to edit text based files, this includes code files like python and other scripts relevant to scientific computing (see :ref:`shell/bash` in Advanced). +Every unix server will have *vim*, which is a powerful text editor, but it does take some getting used to. For example, unlike in 'MS Word' or editors with *GUIs*, you cannot click to place your cursor in vim. + +You can try out this `tutorial `_. Note: I have found that you can usually also use the `up/down/left/right` keys to navigate the text, not just `h/j/k/l` as is stated here. You can practise freely using terminal (Mac/Linux) or the unix `emulator `_. + +To open vim type on the command line: + +``vim test.txt`` (if you’re lazy like me ``vi test.txt`` also works). + +This opens a new text file in vim. You can edit an existing file in exactly the same way: + +``vim alreadyexisted.txt`` + +vim is not the only option! *emacs* is a similar editor which is guaranteed to be installed. emacs is opened in the same way as vim. + +Perhaps a better option is *gedit*. This is a simple text editor with a GUI which is usually installed on servers (it is on the GV machines). **This is what I would recommend using as a beginner, if available**. It’s a little clunky but more intuitive to use then the previous options. It is opened exactly the same as vim/emacs. In some cases you may need to set up *X11 forwarding* (see :ref:`X11 forwarding` in Intermediate). + +As you get more comfortable with this scientific computing, you will likely find that you prefer a different text editor with a GUI which is far more user friendly. However, it will require a bit of setting up. Talk to your supervisor/research group about what they use and how they got it to work. + +Intermediate +--------------- + +If the user will be running multiple experiments on their own and analysing the output, the following will likely be useful to them: + +Isca Structure +^^^^^^^^^^^^^^ +It may be useful for you to have a rough idea on how Isca works. The best way to do this is to look through the Isca `documentation `_, especially the Isca structure page. You can also skim through the `source code `_, to get an idea of what files there are – there are lots, but you don’t need to worry about how they all work so do not be intimidated! + +Conda +^^^^^ +As mentioned earlier in the Python section, often Python libraries have to be installed, and you’ll need different libraries depending on what you’re doing. Python *environments* are very useful as loading them will load all the libraries you need for a given task. For example, there is an isca environment which is set up during the Isca installation, which has all the relevant python modules for running Isca. See `here `_ for more details. + +Workstations +^^^^^^^^^^^^ +Some terminology things to be aware of when running on servers/workstations: + +- Workstations (for example the ‘GV machines’ at Exeter) have *cores* which are like groups of processors. So when running Isca you can run on a number of cores, generally the more cores the faster. Due to the way Isca works, you can only run on a number of cores that is a power of 2 (1, 2, 4, 8, 16, 32). We usually run at 8 or 16. +- Unix has a feature called *screen* which allows you to leave something running and logout of a computer. When you’re logged in, simply type ``screen`` on the command line and a screen will start. You can then press ``CTRL+A+D`` to detach from the screen but leave your job running. Then you can log out of the computer. See `here `_ for commands about reattaching, listing screens etc. +- Typing ``top`` on the command line will display a list of users/jobs that are happening at that time. This is useful to make sure you are not overloading the computer. For example, if you wanted a to run an 8 core job but the computer only had 4 cores free, you’d have to wait. + +X11 forwarding +^^^^^^^^^^^^^^ +If you want to make plots and view them from a computer you have SSH’d into, you might need to set up some sort of *X11 forwarding*. It just allows images created in windows on another computer to appear as windows on your own computer. + +Use software like `XQuartz `_ for macOS or Xming for Windows. You’ll also need to add the ``-Y`` or ``-X`` option to your ssh command (i.e. ``ssh –Y user@emps-gv1.ex.ac.uk``) . Getting it set up the first time may be a little tricky, but there is plenty of help available on google/your supervisor. + +netCDFs +^^^^^^^ +Isca has to store the data it generates so that you can analyse it and make plots. The file type it uses is called a *netCDF* file which has a *.nc* suffix. For example, every month Isca can output a file called ``atmos_monthly.nc`` which contains all the variables asked for in the python run script (wind velocities, temperature, precipitation, etc). They are very useful for climate data because it allows variables to be stores on sets of *axis* like latitude, longitude, height* and time. This makes it easy to make plots and there are python libraries e.g. *netCDF4* which have many useful functions to make your life easier. + +If you’re interested there is reams of documentation `here `_ but again, you don't need to understand it too much in order to use it. + +*Note: In Isca’s case the ‘height’ axis is not measured in meters, but usually in `sigma pressure coordinates `_. + +Plotting/xarray +^^^^^^^^^^^^^^^ +When Isca has finished it’s model run, you’ll want to look at the data created and analyse it and make plots. We have some scripts that will help get you started `here `_. These scripts are written using functions from python libraries called `xarray `_, which is a very powerful way to work with datasets in python, and `matplotlib `_ which is a plotting library. You will need to install these libraries to a python environment to use them. + +Transferring Files (SFTP/SCP) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Now you have made plots – or indeed any file you want to transfer between the computer you have SSH’d into and your own – you will need a way of transferring them. There are several ways of doing this. + +*SFTP (SSH File Transfer Protocol)* is one, it will work on all operating systems and is the easiest for windows. One way of using SFTP is with an *SFTP client*, many are available. One of them is `Cyberduck `_. It will require setting up but it is fairly straight forward. These clients tend to have a GUI so you can just drag and drop the files you want to transfer. It is also possible to view and transfer files using the native file browser if you're using Linux or macOS, using their built-in functions to connect via SFTP. + +Other option is to use a command line function, for example ``scp``. This is a secure file copy protocol, which uses SSH. The usage is simple, for example on the computer you want to transfer the file to, type: + +``scp USER@COMPUTERNAME.ex.ac.uk:/path_to_file/file.png /path_to_destination/`` + +This uses the protocol to SSH into the computer with the file and copy it to the location specified on the RHS. Note to copy a directory you can use the ``-r`` (*recursion*) option. We also can use a ``.`` to copy to our current file location. + +``scp –r USER@COMPUTERNAME.ex.ac.uk:/path_to_directory/ ./`` + +See `here `_ for more details. + +Advanced +-------- + +Users who either intend to make changes to the Isca source code, or will use the model so often as to benefit from additional tools, should research the following: + +Git +^^^ +Git is a *version control software*, which allows you and every other user to have different copies of the Isca source code and modify it safely. Developers of Isca will have different *branches* on their own *fork*, which they can modify and improve. If the improvements are useful to everyone, the changes can be added to the `master copy `_. + +Here is a `video `_ about how git works. Here is a useful `cheat sheet `_ on git commands. + +Supercomputers +^^^^^^^^^^^^^^ +You may be able to run Isca on a supercomputer, for example at Exeter we have *ISCA HPC (High Performance Computer)* - the same name get’s confusing. Your supervisor will help get you set up on this as they are a little more complicated, although usually faster. + +When you login to a supercomputer you are in fact logging in to a small *login node* which is not designed to run code. It is designed to allow you to submit your job to a *queue* which will then be run on the main computer (see :ref:`Slurm` below). Here is some `documentation for ISCA HPC `_, see the ISCA User Guide. + +Slurm +^^^^^ +Submitting jobs to a queue requires you to use the supercomputers *workload manager*. ISCA HPC uses *Slurm*, but there is also *moab*. See here for a `slurm cheat sheet `_. The important ones are ``sbatch`` and ``squeue``. + +FORTRAN +^^^^^^^ +The actual Isca model is written in a coding language called *FORTRAN.90*. Therefor if you intend on modifying the source code, you’ll need to know a little FORTRAN. It is incredibly fast, but it has to be *compiled* before use (it is a *low level* language) and is slightly different from *high level* code. For example, you have to define variables before you can use them. There are plenty of FORTRAN tutorials around, e.g. `here `_, however you will probably learn as you go by modifying the Isca code. + +Shell Scripts +^^^^^^^^^^^^^ +A *shell script* (``scriptname.sh``) is a useful tool if you have a series of command lines you have to write, especially if you do it often. For example, I have a shell script that transfers data from one server to another. The `example file `_ to submit a job to ISCA HPC is also a shell script. See `here `_ for more details or google. + +.bashrc Script (aliases) +^^^^^^^^^^^^^^^^^^^^^^^^ +One particular shell script is your ``.bashrc`` script, see `here `_. Your supervisor will set this up for you, as some Isca file locations need to be included in it. One very useful thing that you can set up in this script is *aliases*. This is where a text string is assigned to a command. + +E.g. the line ``alias go_data='cd /scratch/USER/data_isca'`` will allow you to go to your data file location, just by typing ``go_data``. + +Or the line ``alias i='source activate isca_env'`` will activate your ``isca`` python environment just by typing ``i``. + +Useful Links +------------ + +- `How to install isca `_ +- Will Seviour's Scripts - useful code designed for `setting up Isca at Bristol university `_ and `analysing data using a python notebook `_ +- `ICPT workshop repo `_ (some lecture slides and analysis scripts). +- The 2018 `paper on Isca's release `_ +- The `Isca Website `_ + +Authors +------- +This documentation was written by Ross Castle with input from the Isca team, notably Penny Maher, Denis Sergeev, Geoff Vallis and Will Seviour. It is hoped that this document will continue to be edited and improved, especially by masters and PhD students. + +Last updated 31/03/2021 diff --git a/docs/source/bve_swe_equations.rst b/docs/source/bve_swe_equations.rst new file mode 100644 index 000000000..10929e792 --- /dev/null +++ b/docs/source/bve_swe_equations.rst @@ -0,0 +1,13 @@ +Barotropic Vorticity and Shallow Water Equation Solvers +======================================================= +We would like to direct people interested into these equation solvers in Isca to the original documentation written by GFDL. + +Barotropic Vorticty Equation +---------------------------- + + `Here `_ + +Shallow Water Equation +---------------------- + + `Here `_ diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst new file mode 100644 index 000000000..c19d54573 --- /dev/null +++ b/docs/source/changelog.rst @@ -0,0 +1,11 @@ +Changelog +========= + +.. default-role:: py:obj + +2.0 +--- + +:Date: TBA + +* Bug fixes ... diff --git a/docs/source/conf.py b/docs/source/conf.py new file mode 100644 index 000000000..5d672fa83 --- /dev/null +++ b/docs/source/conf.py @@ -0,0 +1,237 @@ +# -*- coding: utf-8 -*- +"""Sphinx configuration.""" +# +# Configuration file for the Sphinx documentation builder. +# +# This file does only contain a selection of the most common options. For a +# full list see the documentation: +# http://www.sphinx-doc.org/en/master/config + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) + +import os +from glob import glob + +# import isca # TODO: uncomment to get version automatically + + +# -- Project information ----------------------------------------------------- + +project = "isca" +copyright = "2020, the Isca development team" # noqa +author = "Isca development team" + +# The short X.Y version +version = "dev" # TODO: add versioneer, use isca.__version__ +# The full version, including alpha/beta/rc tags +release = version + + +# -- General configuration --------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +# +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + "sphinx.ext.extlinks", + "sphinx.ext.intersphinx", + "sphinx.ext.mathjax", + "sphinx.ext.viewcode", + "sphinx.ext.napoleon", + "nbsphinx", + "sphinx.ext.autosectionlabel" +] +napoleon_numpy_docstring = True +napoleon_include_init_with_doc = False + +# Add any paths that contain templates here, relative to this directory. +templates_path = ["_templates"] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +# source_suffix = ['.rst', '.md'] +source_suffix = ".rst" + +# The master toctree document. +master_doc = "index" + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = [] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = "sphinx" +highlight_language = "python3" + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. + +html_theme_path = ["_themes"] + +# import sphinx_rtd_theme # noqa +html_theme = "sphinx_rtd_theme" +# html_theme = 'sphinxdoc' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# +# html_theme_options = {} + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ["_static"] + +# Custom sidebar templates, must be a dictionary that maps document names +# to template names. +# +# The default sidebars (for documents that don't match any pattern) are +# defined by theme itself. Builtin themes are using these templates by +# default: ``['localtoc.html', 'relations.html', 'sourcelink.html', +# 'searchbox.html']``. +# +# html_sidebars = {} + +# Site icon +html_favicon = "_static/favicon.ico" + + +# -- Options for HTMLHelp output --------------------------------------------- + +# Output file base name for HTML help builder. +htmlhelp_basename = "iscadoc" + + +# -- Options for LaTeX output ------------------------------------------------ + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [(master_doc, "isca.tex", "Isca Docs", author, "manual")] + + +# -- Options for manual page output ------------------------------------------ + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [(master_doc, "isca", "Isca Documentation", [author], 1)] + + +# -- Options for Texinfo output ---------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ( + master_doc, + project, + "Isca Documentation", + author, + "Isca", + "One line description of project.", + "Miscellaneous", + ) +] + + +# -- Options for Epub output ------------------------------------------------- + +# Bibliographic Dublin Core info. +epub_title = project + +# The unique identifier of the text. This can be a ISBN number +# or the project homepage. +# +# epub_identifier = '' + +# A unique identification for the text. +# +# epub_uid = '' + +# A list of files that should not be packed into the epub file. +epub_exclude_files = ["search.html"] + + +# -- Extension configuration ------------------------------------------------- + +# -- Options for intersphinx extension --------------------------------------- +# Options for intersphinx. +intersphinx_mapping = { + "matplotlib": ("https://matplotlib.org", None), + "numpy": ("https://numpy.org/doc/stable", None), + "pandas": ("http://pandas.pydata.org/", None), + "python": ("https://docs.python.org/3/", None), + "xarray": ("https://xarray.pydata.org/en/stable", None), +} +nitpick_ignore = [("py:class", "optional"), ("py:class", "array-like")] + +# -- Autodoc settings -- +autodoc_default_options = { + "autoclass_content": "both", + "member-order": "bysource", + "members": None, + "special-members": "__init__", + "show-inheritance": None, +} + + +# nbsphinx linking +examples_src_dir = "../../examples" +examples_doc_dir = "examples" +examples_header = "_index_header_rst" + +for nb_f in glob(os.path.join(examples_doc_dir, "*.ipynb")): + if os.path.islink(nb_f): + os.unlink(nb_f) + +with open(os.path.join(examples_doc_dir, "index.rst"), "w") as fw: + with open(os.path.join(examples_doc_dir, examples_header), "r") as fr: + fw.write(fr.read()) + fw.write("\n") + for nb_f in sorted(glob(os.path.join(examples_src_dir, "*.ipynb"))): + _bn = os.path.basename(nb_f) + if _bn.startswith("Untitled"): + continue + os.symlink(os.path.abspath(nb_f), os.path.join(examples_doc_dir, _bn)) + fw.write(" {}\n".format(_bn)) diff --git a/docs/source/contributing.rst b/docs/source/contributing.rst new file mode 100644 index 000000000..ea3f8937d --- /dev/null +++ b/docs/source/contributing.rst @@ -0,0 +1,40 @@ +Contributor's Guide +=================== + +Isca takes contributions using pull requests on `GitHub `_. + +Creating a Github fork +---------------------- +1. Fork Isca on Github, so a copy of it appears in your Github profile and has a remote URL like `https://github.com/your_user_name/Isca `_. +2. Git-clone it to your computer using the new URL (or change the remote of your existing folder). +3. It is better to create a new branch (e.g. :code:`git checkout -b your_new_branch`). If your contribution is relatively small, such as fixing one small bug or typo, you can skip this step. +4. Make changes, commit them and push to *your* remote. (Which again, should have your URL. You can check it by typing :code:`git remote -v`). +5. Run your own tests to make sure you're happy. +6. Run Isca's built-in 'trip-tests', and take note of the results. See :doc:`./testing/trip_test` for guidance on how to do this. +7. Go to your Isca page on Github and create a pull request (a prompt for this should show up on top of the page). + + +Contributing to the documentation +--------------------------------- +We welcome contributions to improve the documentation of Isca. + +1. Please make yourself familiar with the `reST formatting guide `_. +2. Follow the steps 1-3 above to create a fork. +3. Make changes + a. If you are adding a new section of documentation, make a copy of the template file (:code:`docs/source/_template.rst`), assign it an appropriate name and place it to the appropriate location within :code:`docs/source/`. Add a link to the newly created file to `index.rst` within the same folder. + b. If you are changing an existing file, proceed. +4. Once you are done, remove all the comments from your .rst file if you are using the template. +5. Do not forget to add any relevant references to papers or textbooks. + a. Create a new entry in :code:`docs/source/references.rst`, following the formatting style of the existing entries. For example, + :: + .. [VallisEtAl2018] Vallis, G. K. and Colyer, G. and Geen, R. and Gerber, E. and Jucker, M. and + Maher, P. and Paterson, A. and Pietschnig, M. and Penn, J. and Thomson, S. I., 2018: + Isca, v1.0: a framework for the global modelling of the atmospheres of Earth and + other planets at varying levels of complexity. *Geoscientific Model Development*, + **11(3)**, 843-859, + doi: `10.5194/gmd-11-843-2018 `_. + + b. Add the citation to your docs page by using the relevant citation key (note the underscore symbol at the end). For example: + :: + We use the Isca model ([VallisEtAl2018]_) +6. Create a pull request and wait for the Isca team to review it. diff --git a/docs/source/examples/_index_header_rst b/docs/source/examples/_index_header_rst new file mode 100644 index 000000000..a95d4b91d --- /dev/null +++ b/docs/source/examples/_index_header_rst @@ -0,0 +1,13 @@ +.. _examples: + +Examples +======== + +Here are few examples on how to run Isca and analyse its output. + +.. note:: + + These examples provide practical ways to illustrate the usage of the software, and do not necessarily represent the best scientific methods for any particular type of study. + +.. toctree:: + :maxdepth: 1 diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100644 index 000000000..3110053aa --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,152 @@ +Overview +======== + +Welcome to Isca! +On this page and those following you will find the documentation to help you understand and run Isca. You can find a briefer and more general overview `here `_ and the code itself is on `github `_. + +About Isca +---------- + +Isca is a framework for the construction of models of the global circulation of the atmosphere of Earth and other planets at varying levels of realism and complexity. The framework uses the dynamical core and the software infrastructure (FMS, for Flexible Modeling System) from the Geophysical Fluid Dynamics Laboratory in Princeton, USA, and some of the physical parameterizations are also taken from there. Isca itself is not a single model, nor is it intended to provide a fully 'comprehensive' model capable of weather forecasts or climate projections for policy use. Rather, our intent is to take the idea of a model hierarchy seriously and thereby enable the user to make appropriate models for the problem at hand, or to make a connected and traceable sequence of models with feedbacks and processes included or denied as appropriate, ranging from the very simple to the near comprehensive. + +.. figure:: index_fig_marspv.gif + :scale: 40 + + Isca simulation of the northern Martian winter polar vortex, showing the evolution of Lait-scaled potential vorticity on the 300K isentropic surface from late autumn (Ls 230) through to late winter (Ls 300). Figure produced by Emily Ball (University of Bristol), on a developmental branch. + +Isca's user friendly Python front end allows users to quickly and easily configure their experiments. Test experiments are included to aid the first-time user in understanding the framework, but can also be used as a basis for more specialised experiments. Because of it flexibility and intuitive interface, Isca now has many users thus creating an Isca community that, with the addition of the documentation you can find here, are able to support fellow researchers with ideas and solutions to problems. The code is open source and downloadable from `github `_, and if you have questions please `raise an issue `_ there. + +The various options for Isca are described in detail in the links in the left column (where appropriate references are also given), and here we just give an overview of what is available. Please note that, since Isca is constantly being developed and improved, not all of the options are as yet fully documented. We hope that you, the user, may also wish to contribute to Isca's development and documentation. + +Options and configurability +--------------------------- + +The main code is written in Fortran 90 to which we provide a Python front end. The front end enables many model configurations, parameter changes and output options to be set without going into the code itself, but if you wish to make more substantial changes then editing the Fortran may be necessary. + +Dynamical cores and variants: +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* Primitive equation, spectral model on the sphere. This may also be configured as a zonally-symmetric model +* Shallow water model on the sphere +* Column model, for example for radiative-convective equilibrium studies +* Barotropic vorticity equation on the sphere + +Radiative forcing +^^^^^^^^^^^^^^^^^ +* Thermal relaxation: Held-Suarez, variations for terrestrial exoplanets, and a 'top--down' scheme that provides a self-consistent determination of the tropopause +* Grey radiation, with or without water vapour feedback +* Simple, two IR band (with an IR window) scheme, with a simple solar scheme +* Comprehensive, multi-band schemes using either SOCRATES or RRTM + +Convection and boundary-layer schemes +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +* A very flexible moist and dry convective adjustment scheme, suitable for both Earth and other planetary atmospheres +* The Betts-Miller scheme (a well-known and much used convective relaxation scheme) +* A mass flux scheme (relaxed Arakawa-Schubert) +* A simple boundary-layer scheme + +Clouds +^^^^^^ +* A simple cloud scheme based on relative humidity and inversion strength. + +Surface properties +^^^^^^^^^^^^^^^^^^ +* Specified sea-surface temperatures +* A slab ocean with a prognostic SST +* A swamp land surface with an evaporative resistance +* A simple bucket model +* A simple vegetation model +* Realistic Earth continental outlines, or user-configurable idealized continental configurations +* User configurable topography + +Test cases +^^^^^^^^^^ +Various out of the box test cases are available that should run with no additional configuration. These include: + +* The Held-Suarez test case +* An aquaplanet with a slab ocean and grey radiation +* A MiMA-like test case using RRTM +* An axisymmetric model +* A gas giant planet (Jupiter-like) +* Cases with realistic, Earth-like continents + +Planets +^^^^^^^ +Earth is, naturally, the planet to which Isca has most often been applied. However, models of varying degrees of sophistication of Mars, Jupiter, Titan, TRAPPIST-1e, and a generic tidally-locked exoplanet have or are being constructed. + +.. figure:: index_fig_trappist.png + :scale: 60 + + Simulations for the `TRAPPIST-1 Habitable Atmosphere Intercomparison (THAI) `_ project. TRAPPIST-1e is a tidally-locked rocky exoplanet and is a promising candidate for habitable surface conditions. Figure produced by Dr Matthew Henry. + +The Future +^^^^^^^^^^ +Isca, and this documentation, is constantly under development. A simple cloud scheme has recently been incorporated into Isca, and models of other planetary bodies are continually being developed. The next major model addition will be a fully interactive ocean as an integrated component of Isca and therefore configurable with the Python interface and flexible in its nature and use. We invite contributions from users to add to its capabilities and improve its documentation. + +.. figure:: index_fig_qbo.png + :scale: 40 + + The Quasi-Biennial Oscillation (QBO), a wave driven periodic reversal of the stratospheric tropical winds on Earth. This was produced in Isca using the convective gravity wave parameterisation modified by Prof. Chaim Garkfinel. Figure produced by Ross Castle. + +How to cite Isca +---------------- +| The primary reference for Isca, and the one that should be cited if you use it, is: +| Vallis, G.K., Colyer, G., Geen, R., Gerber, E., Jucker, M., Maher, P., Paterson, A., Pietschnig, M., Penn, J. and Thomson, S.I., 2018. Isca, v1. 0: A framework for the global modelling of the atmospheres of Earth and other planets at varying levels of complexity. *Geosci. Model Dev.*, 11, 843–859. +| +| In addition, if you use particular aspects of Isca please consider referencing one of the following papers: +| +| For use of the SOCRATES radiation scheme: +| Thomson, S.I. Vallis, G.K., 2019. The effects of gravity on the climate and circulation of a terrestrial planet. *Quarterly Journal of the Royal Meteorological Society*, 145, pp. 2627–2640. +| +| To emphasize the hierarchical aspects of Isca, or if using the Mars, Jupiter or shallow water models: +| Thomson, S.I. and Vallis, G.K., 2019. Hierarchical Modeling of Solar System Planets with Isca. *Atmosphere,* 10, p.803. +| +| For a general discussion of the value of a hierarchical approach: +| Maher, P., Gerber, E.P., Medeiros, B., Merlis, T.M., Sherwood, S., Sheshadri, A., Sobel, A.H., Vallis, G.K., Voigt, A. and Zurita‐Gotor, P., 2019. Model hierarchies for understanding atmospheric circulation. *Rev. Geophysics,* 57, pp.250-280. +| +| For a simple surface model: +| Pietschnig, M., Lambert, F.H., Saint‐Lu, M. and Vallis, G.K., 2019. The presence of Africa and limited soil moisture contribute to future drying of South America. *Geophysical Research Letters,* 46(21), pp.12445-12453. +| +| For examples of different land configurations: +| Geen, R., Lambert, F.H. and Vallis, G.K., 2018. Regime change behavior during Asian monsoon onset. *Journal of Climate,* 31(8), pp.3327-3348. +| +| For the MiMA configuration of Isca, with the RRTM radiation scheme: +| Jucker, M. and Gerber, E.P., 2017. Untangling the annual cycle of the tropical tropopause layer with an idealized moist model. *Journal of Climate,* 30(18), pp.7339-7358. +| +| For exoplanets: +| Penn, J. and Vallis, G.K., 2018. Atmospheric Circulation and Thermal Phase-curve Offset of Tidally and Nontidally Locked Terrestrial Exoplanets. *The Astrophysical Journal,* 868(2), p.147. + +Contents +======== +.. toctree:: + :maxdepth: 1 + :glob: + + install + begginers_guide + examples/index.rst + isca_structure + modules/index.rst + python + changelog + contributing + testing/index.rst + bve_swe_equations + references + remote_access + +See also +======== + +- Isca `home page `_ +- Isca v1.0 model description: [VallisEtAl2018]_ + +License +======= +Isca is released under the terms of the `GPL-3.0 license `_. + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`search` diff --git a/docs/source/index_fig_marspv.gif b/docs/source/index_fig_marspv.gif new file mode 100644 index 000000000..f656dc046 Binary files /dev/null and b/docs/source/index_fig_marspv.gif differ diff --git a/docs/source/index_fig_qbo.png b/docs/source/index_fig_qbo.png new file mode 100644 index 000000000..9754e4ff1 Binary files /dev/null and b/docs/source/index_fig_qbo.png differ diff --git a/docs/source/index_fig_trappist.png b/docs/source/index_fig_trappist.png new file mode 100644 index 000000000..d248437f8 Binary files /dev/null and b/docs/source/index_fig_trappist.png differ diff --git a/docs/source/install.rst b/docs/source/install.rst new file mode 100644 index 000000000..4537dec6b --- /dev/null +++ b/docs/source/install.rst @@ -0,0 +1,70 @@ +.. _install: + +Installation +============ + +Supported language: + +- Python: 3.7 + +.. note:: + + We highly recommend installing and using the free `Anaconda + `_ distribution of Python (or + `Miniconda `_, if you don't want + all of the extra packages that come built-in with Anaconda), which + works on Mac, Linux, and Windows, both on normal computers and + institutional clusters and doesn't require root permissions. + +Isca depends on the following libraries + +- f90nml +- fortran-compiler +- jinja2 +- libgfortran +- netcdf-fortran +- numpy +- openmpi +- pandas +- python=3.7 +- pip +- pytest +- sh +- tqdm +- xarray + +After the required packages are installed, Isca can be installed from source. + + +Clone from Github +----------------- + +You can obtain it directly from the `Github repo `_ :: + + git clone https://www.github.com/execlim/isca.git + cd isca/src/extra/python + pip install -e . + +Verifying proper installation +----------------------------- + +Once installed via any of these methods, you can run isca's suite of +tests using `py.test `_. From the top-level +directory of the isca installation :: + + conda install pytest # if you don't have it already; or 'pip install pytest' + py.test + +If the pytest command results in any error messages or test failures, +something has gone wrong, and please refer to the Troubleshooting +information below. + +Troubleshooting +--------------- + +If you're having compiler issues please speak to your computing officer. We've tried and tested Intel Fortran, GFortran (usually works) and Cray-Fortran. NetCDF compilers can also be an issue too. + +Please search through the `Issues page`_ on Github if anybody else has had the same problem you're facing. +If none do, then please send open a new Issue. + +.. _Issues page: https://github.com/execlim/isca/issues diff --git a/docs/source/isca_structure.rst b/docs/source/isca_structure.rst new file mode 100644 index 000000000..649066176 --- /dev/null +++ b/docs/source/isca_structure.rst @@ -0,0 +1,29 @@ +Isca's Code Structure +===================== + +This page briefly shows the general order in which Isca's modules are called when running the code. It would be too complicated to show every single Fortran file, and the exact formulation would be different depending on the model configuration. As Isca is structured a little differently to other spectral climate models, we show a more general description of a spectral model at the end of this page. + +Isca Structure +-------------- +For ease and accessibility, Isca is controlled using a python interface which sets up the experiment runs, e.g. ``frierson_test_case.py``. This then calls the atmosphere model (``atmos_model.F90``) which essentially just controls the model run, calling the atmosphere module (``atmosphere.F90``) every timestep. This module in turn calls the physical parameterisations from the physics driver (usually ``idealized_moist_phys.F90``), the spectral dynamical core (``spectral_dynamics.F90``) and the vertical coordinate driver (``press_and_geopot.F90``). Most of the lower layer code, which is where the Isca's many different options are located, is called from the physics driver. This includes radiation schemes, convection schemes, damping and diffusion. + +Note: If using grey radiation, the surface flux is calculated after the up sweep in the grey radiation code, but before the down sweep. In addition for any configuration, the mixed layer ocean code is called after the down sweep in the vertical diffusion code, but before the up sweep. + +.. figure:: isca_structure_diagram_edited.png + :scale: 20 + + Flowchart showing the order of modules in the Isca code. No particular set up has been chosen here, for example, all possible calls to the radiation and convection parameterisations are shown, when a model run would only call 1. + +Comparison To A General Spectral Model +-------------------------------------- + +Our comparison here is based on the 2006 release of the GDFL CM2 spectral model. Because these models have an atmosphere ocean coupler, the model runs in a different, and slightly more complicated way. Note the multiple loops that the code must run through to do an up and down sweep. In Isca, because the mixed layer ocean is defined on that atmosphere grid, we can avoid these steps, meaning the model is easier to understand and use. + +.. figure:: other_model_structure_diagram.png + :scale: 20 + + Flowchart showing the order of modules in a 'normal' GCM. Note this is a simplification of the call structure, as the subsequent parameterisations are not included here. + +Authors +------- +This documentation was written by Ross Castle and reviewed by Penelope Maher and Stephen Thomson. \ No newline at end of file diff --git a/docs/source/isca_structure_diagram_edited.png b/docs/source/isca_structure_diagram_edited.png new file mode 100644 index 000000000..20af65bfc Binary files /dev/null and b/docs/source/isca_structure_diagram_edited.png differ diff --git a/docs/source/modules/constants.rst b/docs/source/modules/constants.rst new file mode 100644 index 000000000..2be2f6685 --- /dev/null +++ b/docs/source/modules/constants.rst @@ -0,0 +1,76 @@ + +Constants: Atmospheric and Planetary Parameters +=============================================== + +Summary +------- + +``constants.F90`` provides a number of controls that set the fundamental parameters of the planet being studied - it is used by nearly every other module in Isca. By default, Isca models a planet equivalent to Earth in terms of physical characteristics such as size, surface gravity and rotational period. In addition, water is set as the default condensate for any moist physics modules that may be used. + +Planetary Parameters +^^^^^^^^^^^^^^^^^^^^ + +Below are a set of controls that allow the physical size and rotation of a planet to be set in the namelist. This allows us to model a range of planets, including others in the Solar System such as Mars and Jupiter, in addition to exoplanets. Rotation can be set by two different parameters: a rotation rate ``omega`` can be specified; or alternatively a period in seconds can be given that is converted back to a value of ``omega``. + ++---------------------+----------------------------------+----------------------------------+-----------------------------------------------------------+ +| Name | Default | Units | Description | ++=====================+==================================+==================================+===========================================================+ +|``radius`` | :math:`6371\times 10^3` | m | Radius of planet | ++---------------------+----------------------------------+----------------------------------+-----------------------------------------------------------+ +|``gravity`` | :math:`9.80` | ms :math:`^{-2}` | Surface gravitational acceleration | ++---------------------+----------------------------------+----------------------------------+-----------------------------------------------------------+ +|``omega`` | :math:`7.2921150\times 10^{-5}` | rad :math:`\cdot` s :math:`^{-1}`| Rotation rate of planet | ++---------------------+----------------------------------+----------------------------------+-----------------------------------------------------------+ +|``orbital_period`` | :math:`31557600` | s | Orbital period of planet | ++---------------------+----------------------------------+----------------------------------+-----------------------------------------------------------+ +|``solar_constant`` | :math:`1368.22` | Wm :math:`^{-2}` | Stellar irradiance | ++---------------------+----------------------------------+----------------------------------+-----------------------------------------------------------+ +|``earthday_multiple``| False | n/a | Modifies seconds per sol calculation (planetary solar day)| ++---------------------+----------------------------------+----------------------------------+-----------------------------------------------------------+ + +By default the parameter ``seconds_per_sol`` (where ``earthday_multiple`` is false) is calculated using + +.. math:: \texttt{seconds_per_sol} = \left|\frac{2\pi}{\texttt{orbital_rate} - \texttt{omega}}\right| , + +whereas setting ``earthday_multiple`` to true modifies the calculation to + +.. math:: \texttt{seconds_per_sol} = 86400 \cdot \frac{\texttt{earth_omega}}{\texttt{omega}} . + +.. note:: Whilst the rotation and orbital rates are set within this module, other parameters associated with planetary motion such as axial tilt (obliquity) and eccentricity are controlled from the ``astronomy_mod`` module. + +Atmospheric Parameters +^^^^^^^^^^^^^^^^^^^^^^ + +Changing the basic atmospheric properties allows atmospheres with a wide range of compositions to be studied, including both terrestrial planets (e.g. Earth/Mars) and gas giants such as Jupiter. In the case of Earth, the reference surface pressure is taken to be the mean sea level pressure. + +The dry air gas constant for any homogeneous atmosphere can be calculated from its chemical composition. It is calculated by dividing the universal gas constant :math:`R` by the average molar mass of the atmosphere. + ++------------+----------------------------+-------------------------------------+-------------------------------------------------------+ +| Name | Default | Units | Description | ++============+============================+=====================================+=======================================================+ +|``pstd_mks``| :math:`101325.0` | Pa / Nm :math:`^{-2}` | Mean (reference) surface pressure (SI) | ++------------+----------------------------+-------------------------------------+-------------------------------------------------------+ +|``pstd`` | :math:`1.013250\times 10^6`| dyn :math:`\cdot` cm :math:`^{-2}` | Mean (reference) surface pressure (cgs) | ++------------+----------------------------+-------------------------------------+-------------------------------------------------------+ +|``rdgas`` | :math:`287.04` | Jkg :math:`^{-1}`K :math:`^{-1}` | Dry air gas constant | ++------------+----------------------------+-------------------------------------+-------------------------------------------------------+ +|``kappa`` | :math:`2/7` | dimensionless | Heat capacity ratio ( :math:`\gamma` for an ideal gas)| ++------------+----------------------------+-------------------------------------+-------------------------------------------------------+ +|``es0`` | :math:`1.0` | dimensionless | Humidity factor :math:`^\dagger` | ++------------+----------------------------+-------------------------------------+-------------------------------------------------------+ + +:math:`^\dagger`: The humidity factor controls the atmospheric humidity content via the expression for saturation vapor pressure *if* ``do_simple: True`` is set within the ``idealized_moist_phys`` namelist. + +.. note:: If the mean surface pressure value is changed here, it is necessary to also set ``reference_sea_level_press`` from the ``spectral_dynamics`` namelist, else the output file will not extend to the pressure specified. + +Moist Atmospheres +^^^^^^^^^^^^^^^^^ +We hope to be able to introduce controls for moist atmospheres in a future update to Isca. This will allow condensable species other than water to be easily modelled. + +Relevant Modules +---------------- +Since this module provides the definition of a number of physical constants, it is used by most other modules that exist within the Isca framework. + +Authors +------- +This documentation was written by Daniel Williams, peer reviewed by Stephen Thompson, and quality controlled by Ross Castle. diff --git a/docs/source/modules/convection_simple_betts_miller.rst b/docs/source/modules/convection_simple_betts_miller.rst new file mode 100644 index 000000000..4e4ebeda7 --- /dev/null +++ b/docs/source/modules/convection_simple_betts_miller.rst @@ -0,0 +1,91 @@ +Convection scheme: Simple Betts-Miller +================================= + + +Summary +------- +The simplified Betts-Miller (SBM) scheme is a moist adjustment convection scheme developed by [Frierson2007]_ and the modifications described in [OGormanSchneider2008]_. The simple Betts-Miller scheme is a widely-used convection scheme in Isca. The key purpose of the routine is to output a moisture tendency ``deltaq``, a temperature tendency ``deltaT``, and a convective rainfall amount ``rain``. The scheme has both deep convection and non-precipitating shallow convection. + + +Logic of the routine +------- + +1. Perform a particle ascent to calculate the environmental vertical profiles of moisture and temperature, and convective available potential energy (CAPE) within a grid cell. + +2. Test if the convection scheme should trigger. The convection scheme will trigger when :math:`CAPE>0`. This means that if the grid point has positive CAPE, then the convention scheme will try to generate rain. If the CAPE is negative, then a parcel will not be buoyant and convection can not occur. + +3. Calculate the temperature (:math:`T_{ref}`) and moisture reference profiles (:math:`q_{ref}`). Think of these profiles as the desirable parcel properties for the atmosphere to be stable (with respect to convection). + +4. Calculate the first guess reference profiles using equations 1-2 from [Frierson2007]_: + +:math:`\partial q = - \frac{q-q_{ref}}{\tau}`, + +:math:`\partial T = - \frac{T-T_{ref}}{\tau}`. + +These profiles are called *first guess* profiles as they will later be corrected (to conserve enthalpy). + +5. Determine if deep convection, shallow convection or no convection occurs in the grid cell (see Section 2 of [Frierson2007]_ for how these are separated). If the parcel profile is unstable, with respect to the reference profile, it will adjust the profiles and generate rain. This adjustment occurs for both the moisture and temperature profiles so that rain is generated when the environment has excess moisture or is cooler compared to the reference profiles). + +6. If deep or shallow convection occurs, test if the vertical integral of enthalpy (:math:`H`) is conserved (it usually is not) and adjust the rainfall and tendencies to conserve enthalpy. Note that :math:`\partial H = -\partial T + \frac{L}{cp}\partial q`. + +7. The final rainfall amount is given by the enthalpy corrected vertical integral of the moisture tendency. The updated tendencies (no longer called *first-guess*) and rainfall are then passed back to ``idealized_moist_phys.F90`` along with other diagnostics. + + +Namelist options +---------------- + +There are five optional namelist variables. + +The first two are directly relevant to how the tendency equations and precipitation are calculated + +:tau_bm: The relaxation time (:math:`\tau`) with the default value of :math:`7200` :math:`s`. +:rhbm: The critical Relative Humidity (RH) used to calculate the moisture reference profile (:math:`RH_{SBM}`) with the default value of :math:`0.8`. + +The final three are used to construct the look up table for calculating the lifting condensation level (LCL). + +:Tmin: The minimum look up table value with the default of :math:`173` :math:`K`. +:Tmax: The maximum look up table value with the default of :math:`335` :math:`K`. +:val_inc: The increment used to linearly interpolate within the look up table with the default of :math:`0.01`. + + +Diagnostics +----------- +The diagnostics are not sent out within the call to convection but are sent out from ``idealized_moist_phys.F90`` with the module name ``atmosphere``. The diagnostics relevant to the simple Betts-Miller scheme are in the table below. + ++-------------------+----------------------------+------------------------------------+ +| Name | Description | Units | ++===================+============================+====================================+ +| dt_qg_convection | Moisture tendency |:math:`kg~kg^{-1}~s^{-1}` | ++-------------------+----------------------------+------------------------------------+ +| dt_tg_convection | Temperature tendency |:math:`K~s^{-1}` | ++-------------------+----------------------------+------------------------------------+ +| convection_rain | Convective precipitation |:math:`kg~m^{-2}~s^{-1}` | ++-------------------+----------------------------+------------------------------------+ +| cape | Convective available |:math:`J~kg^{-1}` | +| | potential energy | | ++-------------------+----------------------------+------------------------------------+ +| cin | Convective inhibition |:math:`J~kg^{-1}` | ++-------------------+----------------------------+------------------------------------+ + + +Relevant modules and subroutines +-------------------------------- + +The convection scheme is located in: ``src/atmos_param/qe_moist_convection/qe_moist_convection.F90``. The files name reflects this style of a quasi-equilibrium (qe) convection scheme (for more information on convective quasi-equilibrium see [YanoPlant2016]_). + +The simple Betts-Miller scheme is initialised and called by: ``/src/atmos_spectral/driver/solo/idealized_moist_phys.F90``. + +Relevant routines which are called by the convection scheme are: +``src/shared/sat_vapor_pres/sat_vapor_pres.F90``. + + + +References +---------- + +The reference list within this docs page are: [Frierson2007]_, [Betts1986]_, [BettsMiller1986]_, [YanoPlant2016]_, and [OGormanSchneider2008]_. + +Authors +---------- + +This documentation was written by Penelope Maher, peer reviewed by Denis Sergeev, and quality controlled by Ross Castle. diff --git a/docs/source/modules/damping_driver.rst b/docs/source/modules/damping_driver.rst new file mode 100644 index 000000000..6e4755b4e --- /dev/null +++ b/docs/source/modules/damping_driver.rst @@ -0,0 +1,141 @@ +Damping Driver +====================== + +Summary +------- +The ``damping_driver`` module is called by the ``idealized_moist_phys`` module by setting ``do_damping`` to true. It controls the upper level momentum damping in Isca. It controls 4 optional functions: + +1. **Rayleigh friction** which acts on levels ``1`` to ``kbot``. This function is located in ``damping_driver`` itself. +2. A (orographic) **mountain gravity wave drag** module (``cg_drag``) may be called. +3. A (non orographic) **convective gravity wave drag** module (``mg_drag``) may be called. +4. A **time independent drag** may be called. This function is located in ``damping_driver`` itself. + +Another module (``topo_drag``) is referenced in the code, but is not available in Isca at present. + +It is located at: ``Isca/src/atmos_param/damping_driver/damping_driver.f90`` + +Namelist options +---------------- +``trayfric`` - (for Rayleigh friction) damping time in seconds for Rayleigh damping momentum in the top model layers, the number of which is specified by ``nlev_rayfric`` (non namelist parameter, automatically determined in code). If ``trayfric`` < 0 then time in days. Default 0. + +``do_rayleigh`` - On/Off switch for doing Rayleigh friction. Default False + +``sponge_pbottom`` - (for Rayleigh friction) used to calculate ``nlev_rayfric``, it specifies the bottom level where the Rayleigh friction starts. Default 50Pa. + +``do_cg_drag`` - On/Off switch for doing mountain gravity wave drag. Default False. + +``do_topo_drag`` - On/Off switch for doing the topo_drag module which is currently unavailable. Default False. + +``do_mg_drag`` - On/Off switch for doing convective gravity wave drag. Default False. + +``do_conserve_energy`` - (for Rayleigh friction) On/Off switch for also calculating the temperature tendency (if True). Default False. + +``do_const_drag`` - On/Off switch for doing the constant drag. Default False. + +``const_drag_amp`` - (for constant drag) Parameter for adjusting drag. Default 3.e-04. + +``const_drag_off`` - (for constant drag) Parameter for adjusting drag. Default 0. + +For a typical idealized Earth set up there is no parameterised gravity wave drag and Rayleigh damping is only needed to keep the model stable. The namelist would then look like:: + ``'do_rayleigh': True, + 'trayfric': -0.25, + 'sponge_pbottom': 50., + 'do_conserve_energy': True`` + +Diagnostics +----------- +There are many diagnostics either in or passed by damping driver. + +**Rayleigh friction:** + ++-----------------------+------------------------------------+------------------------+ +| Name | Description | Units | +| | | | ++=======================+====================================+========================+ +| ``udt_rdamp`` | u wind tendency |:math:`m s^{-2}` | ++-----------------------+------------------------------------+------------------------+ +| ``vdt_rdamp`` | v wind tendency |:math:`m s^{-2}` | ++-----------------------+------------------------------------+------------------------+ +| ``tdt_diss_rdamp`` | dissipative heating |:math:`K s^{-1}` | ++-----------------------+------------------------------------+------------------------+ +| ``diss_heat_rdamp`` | integrated dissipative heating |:math:`W m^{-2}` | ++-----------------------+------------------------------------+------------------------+ + +**Mountain GWD:** + ++-----------------------+------------------------------------+------------------------+ +| Name | Description | Units | +| | | | ++=======================+====================================+========================+ +| ``udt_gwd`` | u wind tendency |:math:`m s^{-2}` | ++-----------------------+------------------------------------+------------------------+ +| ``vdt_gwd`` | v wind tendency |:math:`m s^{-2}` | ++-----------------------+------------------------------------+------------------------+ +| ``taubx`` | x base flux |:math:`kg m^{-1} s^{-2}`| ++-----------------------+------------------------------------+------------------------+ +| ``tauby`` | y base flux |:math:`kg m^{-1} s^{-2}`| ++-----------------------+------------------------------------+------------------------+ +| ``taus`` | saturation flux |:math:`kg m^{-1} s^{-2}`| ++-----------------------+------------------------------------+------------------------+ +| ``tdt_diss_gwd`` | dissipative heating |:math:`K s^{-1}` | ++-----------------------+------------------------------------+------------------------+ +| ``diss_heat_gwd`` | integrated dissipative heating |:math:`W s^{-2}` | ++-----------------------+------------------------------------+------------------------+ +| ``sgsmtn`` | sub-grid scale topography variance |:math:`m` | ++-----------------------+------------------------------------+------------------------+ + +**Convective GWD:** + ++-----------------------+------------------------------------+------------------------+ +| Name | Description | Units | +| | | | ++=======================+====================================+========================+ +| ``udt_cgwd`` | u wind tendency |:math:`m s^{-2}` | ++-----------------------+------------------------------------+------------------------+ + +**Constant Drag:** + ++-----------------------+------------------------------------+------------------------+ +| Name | Description | Units | +| | | | ++=======================+====================================+========================+ +| ``udt_cnstd`` | u wind tendency |:math:`m s^{-2}` | ++-----------------------+------------------------------------+------------------------+ + +**topo_drag:** + +Note: These are not currently available + ++-----------------------+------------------------------------+------------------------+ +| Name | Description | Units | +| | | | ++=======================+====================================+========================+ +| ``udt_topo`` | u wind tendency |:math:`m s^{-2}` | ++-----------------------+------------------------------------+------------------------+ +| ``vdt_topo`` | v wind tendency |:math:`m s^{-2}` | ++-----------------------+------------------------------------+------------------------+ + +Relevant modules and subroutines +-------------------------------- +The code is split into 4 subroutines; ``damping_driver``, ``damping_driver_init``, ``damping_driver_end`` and ``rayleigh``. The ``_init`` and ``_end`` subroutines are for initializing and closing the module. The majority of the ``damping driver`` code is just a switchboard with the exception of the Rayleigh and constant drag calculations. The calculations for the other drag schemes are given in their own documentation. + +**Rayleigh Drag** + +Located in the ``rayleigh`` subroutine. This code damps the momentum toward zero in the specified upper model levels. The zonal/meridional tendency for each grid cell is calculated my multiplying the zonal/meridional velocity by a factor determined by the pressure and Rayleigh parameters. The damping is therefor proportional to the wind velocity. + +The temperature tendency is calculated using the wind velocities, wind tendencies, and the heat capacity of air. + +**Constant Drag** + +Located in the ``damping_driver`` subroutine. This is modelled on Alexander-Dunkerton winter average, it uses a 3rd order polynomial and the constant drag parameters to calculate a time invariant drag. This set up is modelled on Earth's atmosphere and therefore not recommended for other planets. + + +References +---------- +Convective Gravity Wave Drag (cg_drag) [Pierrehumbert1986]_ + +Orographic Gravity Wave Drag (mg_drag) [Alexander1999]_ + +Authors +------- +This documentation was written by Ross Castle, peer reviewed by Stephen Thomson, and quality controlled by Matthew Henry. diff --git a/docs/source/modules/diag_manager_mod.rst b/docs/source/modules/diag_manager_mod.rst new file mode 100644 index 000000000..a955778f3 --- /dev/null +++ b/docs/source/modules/diag_manager_mod.rst @@ -0,0 +1,162 @@ +Diagnostics Manager Module & Saving model output +============== + +Summary +------- + +This module handles the writing of diagnostic output to netCDF files. The user can specify which fields should be output and at which temporal resolution (e.g. monthly means, daily means ... ). The source code is located at ``src/shared/diag_manager/diag_manager.F90``. + + +Namelist options +---------------- + ++--------------------------------+----------+-----------------------------------------------------------------------------------------+ +| Name | Default | Description | ++================================+==========+=========================================================================================+ +|``append_pelist_name`` | False | Decides whether to append the pelist_name to file name | ++--------------------------------+----------+-----------------------------------------------------------------------------------------+ +|``mix_snapshot_average_fields`` | False | Allow both time average and instantaneous fields in the same output file | ++--------------------------------+----------+-----------------------------------------------------------------------------------------+ +|``max_files`` | 31 | Sets the maximum number of output files allowed | ++--------------------------------+----------+-----------------------------------------------------------------------------------------+ +|``max_output_fields`` | 300 | Sets the maximum number of output fields allowed | ++--------------------------------+----------+-----------------------------------------------------------------------------------------+ +|``max_input_fields`` | 300 | Sets the maximum number of input fields allowed | ++--------------------------------+----------+-----------------------------------------------------------------------------------------+ +|``max_axes`` | 60 | Sets the maximum number of independent axes | ++--------------------------------+----------+-----------------------------------------------------------------------------------------+ +|``do_diag_field_log`` | False | to log the registration of the data field, put the OPTIONAL parameter | +| | | ``do_not_log`` = ``False`` and the namelist variable ``do_diag_field_log`` to ``True`` | ++--------------------------------+----------+-----------------------------------------------------------------------------------------+ +|``write_bytes_in_files`` | False | Write out the number of bytes of data saved to this file | ++--------------------------------+----------+-----------------------------------------------------------------------------------------+ +|``debug_diag_manager`` | False | When set to true, bounds are checked in ``send_data`` (send data to output fields) | ++--------------------------------+----------+-----------------------------------------------------------------------------------------+ +|``max_num_axis_sets`` | 25 | Set maximum number of axes for output (e.g. time and space axes) | ++--------------------------------+----------+-----------------------------------------------------------------------------------------+ +|``use_cmor`` | False | Let the ``diag_manager`` know if the missing value (if supplied) should be overridden | +| | | to be the CMOR standard value of -1.0e20 | ++--------------------------------+----------+-----------------------------------------------------------------------------------------+ +|``issue_oor_warnings`` | True | If ``True`` check for values outside the valid range. This range is passed to the | +| | | ``diag_manager_mod`` via the OPTIONAL variable range in the | +| | | ``register_diag_field`` function | ++--------------------------------+----------+-----------------------------------------------------------------------------------------+ +|``oor_warnings_fatal`` | True | If ``True`` issue a fatal error if any values for the output field are outside the | +| | | given range | ++--------------------------------+----------+-----------------------------------------------------------------------------------------+ + + +Diagnostics +----------- + +This part of the code does not have its own diagnostics, but rather handles the saving of all variables. See also ``/src/extra/python/isca/diagtable.py`` + +Output files +^^^^^^^^^^^^ + +In order to save output in Isca, an output file is created first in your experiment runscript (see examples in ``/exp/test_cases/``). Commonly used output timesteps include monthly, daily or x-hourly. + +``diag.add_file('atmos_monthly', 30, 'days', time_units='days')`` +``diag.add_file('atmos_daily', 1, 'days', time_units='days')`` +``diag.add_file('atmos_6_hourly', 6, 'hours', time_units='hours')`` + +The frequency at which data is saved can be set in the ``main_nml``: :: + + 'main_nml': { + 'dt_atmos': 600, + 'days': 30, + 'calendar': 'thirty_day' + } + +For example, set ``'days': 15`` and ``'calendar': 'fifteen_day'``. + + +Output fields +^^^^^^^^^^^^^ + +An output field is created via ``diag.add_field(module, name, time_avg, files)`` in the experiment runscript. +The default for ``time_avg`` = False, the default for ``files`` = None. +``time_avg`` is usually set to True for most variables when an output field is defined. + +If ``files`` = None, then the diagnostics will be saved to all of the given output files (in our example monthly, daily and 6h). +An output file can be specified via e.g. ``files=['atmos_6_hourly']`` in +``diag.add_field('dynamics', 'ucomp', time_avg=True, files=['atmos_6_hourly'])`` if 6h zonal winds shall be saved, but not monthly/daily + + + +Below is a list of commonly saved diagnostics. See the relevant modules for an exhaustive list of available diagnostics. + ++--------------------------+----------------------+-------------------------+---------------------------------------------+--------------------+ +| Module | Name | Dimensions | Description | Units | ++==========================+======================+=========================+=============================================+====================+ +| ``dynamics`` | ``ps`` | (time, lat, lon) | surface pressure | :math:`Pa` | ++--------------------------+----------------------+-------------------------+---------------------------------------------+--------------------+ +| ``dynamics`` | ``bk`` | (phalf) | vertical coordinate sigma values | :math:`Pa` | ++--------------------------+----------------------+-------------------------+---------------------------------------------+--------------------+ +| ``dynamics`` | ``pk`` | (phalf) | vertical coordinate pressure values | :math:`Pa` | ++--------------------------+----------------------+-------------------------+---------------------------------------------+--------------------+ +| ``dynamics`` | ``slp`` | (time, lat, lon) | sea level pressure | :math:`Pa` | ++--------------------------+----------------------+-------------------------+---------------------------------------------+--------------------+ +| ``dynamics`` | ``height`` | (time, pfull, lat, lon) | geopotential height at full model levels | :math:`m` | ++--------------------------+----------------------+-------------------------+---------------------------------------------+--------------------+ +| ``dynamics`` | ``zsurf`` | (lat, lon) | geopotential height at the surface | :math:`m` | ++--------------------------+----------------------+-------------------------+---------------------------------------------+--------------------+ +| ``dynamics`` | ``u_comp`` | (time, pfull, lat, lon) | zonal component of the horizontal winds | :math:`m/s` | ++--------------------------+----------------------+-------------------------+---------------------------------------------+--------------------+ +| ``dynamics`` | ``v_comp`` | (time, pfull, lat, lon) | meridional component of the horizontal winds| :math:`m/s` | ++--------------------------+----------------------+-------------------------+---------------------------------------------+--------------------+ +| ``dynamics`` | ``omega`` | (time, pfull, lat, lon) | vertical velocity | :math:`Pa/s` | ++--------------------------+----------------------+-------------------------+---------------------------------------------+--------------------+ +| ``dynamics`` | ``sphum`` | (time, pfull, lat, lon) | specific humidity | :math:`kg/kg` | ++--------------------------+----------------------+-------------------------+---------------------------------------------+--------------------+ +| ``dynamics`` | ``temp`` | (time, pfull, lat, lon) | temperature | :math:`K` | ++--------------------------+----------------------+-------------------------+---------------------------------------------+--------------------+ +| ``dynamics`` | ``vor`` | (time, pfull, lat, lon) | vorticity | :math:`1/s` | ++--------------------------+----------------------+-------------------------+---------------------------------------------+--------------------+ +| ``dynamics`` | ``div`` | (time, pfull, lat, lon) | divergence | :math:`1/s` | ++--------------------------+----------------------+-------------------------+---------------------------------------------+--------------------+ +| ``atmosphere`` | ``precipitation`` | (time, lat, lon) | precipitation from resolved, parameterised | :math:`kg/(m^2 s)` | +| | | | and snow | | ++--------------------------+----------------------+-------------------------+---------------------------------------------+--------------------+ +| ``atmosphere`` | ``rh`` | (time, pfull, lat, lon) | relative humidity | :math:`\%` | ++--------------------------+----------------------+-------------------------+---------------------------------------------+--------------------+ +| ``mixed-layer`` | ``t_surf`` | (time, lat, lon) | surface temperature | :math:`K` | ++--------------------------+----------------------+-------------------------+---------------------------------------------+--------------------+ +| ``mixed-layer`` | ``flux_t`` | (time, lat, lon) | sensible heat flux at the surface (up) | :math:`W/m^2` | ++--------------------------+----------------------+-------------------------+---------------------------------------------+--------------------+ +| ``mixed-layer`` | ``flux_lhe`` | (time, lat, lon) | latent heat flux at the surface (up) | :math:`W/m^2` | ++--------------------------+----------------------+-------------------------+---------------------------------------------+--------------------+ +| ``rrtm_radiation`` | ``flux_sw`` | (time, lat, lon) | net shortwave flux at the surface (down) | :math:`W/m^2` | ++--------------------------+----------------------+-------------------------+---------------------------------------------+--------------------+ +| ``rrtm_radiation`` | ``flux_lw`` | (time, lat, lon) | longwave flux at the surface (down only) | :math:`W/m^2` | ++--------------------------+----------------------+-------------------------+---------------------------------------------+--------------------+ + + +Relevant modules and subroutines +-------------------------------- + +The ``diag_manager_mod`` uses several modules and subroutines, including + +* ``diag_axis`` +* ``diag_grid`` +* ``diag_output`` +* ``diag_util`` +* ``diag_data`` +* ``diag_table`` + + +.. References +.. ---------- +.. .. +.. Add relevant references. This is done in 2 steps: +.. 1. Add the reference itself to docs/source/references.rst +.. 2. Insert the citation key here, e.g. [Vallis2017]_ + +.. See the Contributing guide for more info. + +.. None + +Authors +------- + +This documentation was written by Marianne Pietschnig, peer reviewed by Stephen Thomson and quality controlled by Ross Castle. diff --git a/docs/source/modules/dynamics.rst b/docs/source/modules/dynamics.rst new file mode 100644 index 000000000..6c741c9d8 --- /dev/null +++ b/docs/source/modules/dynamics.rst @@ -0,0 +1,8 @@ +Isca's dynamical core +===================== + +Coming soon... + +References +---------- +[Vallis2017]_ diff --git a/docs/source/modules/idealised_moist_phys.rst b/docs/source/modules/idealised_moist_phys.rst new file mode 100644 index 000000000..6a3f8b9ae --- /dev/null +++ b/docs/source/modules/idealised_moist_phys.rst @@ -0,0 +1,299 @@ +.. DO NOT MODIFY THIS FILE UNLESS YOU ARE A CORE MAINTAINER OF ISCA! + +.. + This is a reStructuredText template file for creating + a new documentation entry for the Isca model. + + Please make a copy of this file with the appropriate file name and place it + to the appropriate location within docs/source/ and start writing. + Once you are done, remove all the comments from your .rst file. + + Here is a guide on reST formatting: + https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html + +.. _idealized_moist_phys: + +Moist physics driver: idealized_moist_phys.F90 +============================================== + +Summary +------- + +``idealized_moist_phys.F90`` calls the various modules associated with Isca's moist physics configurations. The specific parameterisations to be used can be selected via namelist input to this module. This is where the radiation, convection, turbulence and land options are set. In addition, timestepping for the bucket hydrology option is managed locally here. + +These options allow users to configure a wide range of planets, including notable configurations from the literature [Frierson2006a]_, [Byrne2013]_, [Schneider2009]_, [Jucker2017]_. Users should bear in mind that the full parameter space is vast and not all options may be compatible with one another. + +The fortran file is found in ``Isca/src/atmos_spectral/driver/solo/idealized_moist_phys.F90`` + + +Namelist options +---------------- +This module controls a large number of switches for different modules. Further information on what each module is for can be found on the module's documentation page. + +NB. Defaults are generally set to ``False`` to avoid accidental use of modules, but this is unlikely to be a useful configuration! For examples of how parameters are set to achieve different model configurations, see the test cases in ``Isca/exp/test_cases/``. + +Humidity, Condensation and Convection +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Four convection schemes, and the option of no convection, are currently available in Isca. The defaults are set so the user should actively select a scheme to use via one of the methods below. Large-scale condensation will be called following the convection scheme in all cases, unless ``DRY_CONV`` is selected. + +Method 1 (preferred) +"""""""""""""""""""" +The scheme to be used can be selected using the ``convection_scheme`` namelist parameter, which may be set to: + ++--------------------------+---------------------------------------------------------------------------+ +|Value |Effect | ++==========================+===========================================================================+ +|``SIMPLE_BETTS_CONV`` |Use Frierson Quasi-Equilibrium convection scheme [Frierson2007]_. | ++--------------------------+---------------------------------------------------------------------------+ +|``FULL_BETTS_MILLER_CONV``|Use the Betts-Miller convection scheme [Betts1986]_, [BettsMiller1986]_. | ++--------------------------+---------------------------------------------------------------------------+ +|``RAS_CONV`` |Use the relaxed Arakawa Schubert convection scheme [Moorthi1992]_. | ++--------------------------+---------------------------------------------------------------------------+ +|``DRY_CONV`` |Use the dry convection scheme [Schneider2006]_. | ++--------------------------+---------------------------------------------------------------------------+ +|``NO_CONV`` |Use no convection scheme. | ++--------------------------+---------------------------------------------------------------------------+ +|``UNSET`` |Model looks through flags listed under Method 2. | ++--------------------------+---------------------------------------------------------------------------+ + +Using the ``convection_scheme`` option will ensure that flags are set consistently for the selected convection scheme, avoiding user error. The default is ``UNSET``. + +Method 2 (legacy) +""""""""""""""""" +Three namelist parameters exist as switches to select the convection options: + ++-------------------+----------------------------------------------------------------------------------+---------+ +| Option | Summary |Default | ++===================+==================================================================================+=========+ +|``lwet_convection``|If true, this is equivalent to specifying ``SIMPLE_BETTS_CONV`` in Method 1. |``False``| ++-------------------+----------------------------------------------------------------------------------+---------+ +|``do_bm`` |If true, this is equivalent to specifying ``FULL_BETTS_MILLER_CONV`` in Method 1. |``False``| ++-------------------+----------------------------------------------------------------------------------+---------+ +|``do_ras`` |If true, this is equivalent to specifying ``RAS_CONV`` in Method 1. |``False``| ++-------------------+----------------------------------------------------------------------------------+---------+ + +If multiple flags are set as True, or all are False, an error will be raised. This method exists for compatibility with configurations pre-dating method 1. Method 1 is preferred for new model configurations. + +Humidity calculation options +"""""""""""""""""""""""""""" ++-------------+------------------------------------------------------------------+---------+ +| Option | Summary |Default | ++=============+==================================================================+=========+ +|``do_simple``|If true, use a simplification when calculating relative humidity. |``False``| ++-------------+------------------------------------------------------------------+---------+ + +Radiation +^^^^^^^^^ +Two more comprehensive radiation codes are currently included in Isca: RRTM [MlawerEtAl1997]_ and Socrates [MannersEtAl2015]_. In addition, a number of simple radiation parameterisations for the atmospheres of Earth and other planets can be found in ``two_stream_gray_rad.F90``. The radiation scheme is set through the following flags: + ++-------------------------+-----------------------------------------------+---------+ +| Option | Summary |Default | ++=========================+===============================================+=========+ +|``two_stream_gray`` |If true, call the two stream radiation module. |``True`` | ++-------------------------+-----------------------------------------------+---------+ +|``do_rrtm_radiation`` |If true, call the RRTM radiation module. |``False``| ++-------------------------+-----------------------------------------------+---------+ +|``do_socrates_radiation``|If true, call the SOCRATES radiation module. |``False``| ++-------------------------+-----------------------------------------------+---------+ + +Drag and Turbulence +^^^^^^^^^^^^^^^^^^^ + +Lower Boundary Heat, Momentum & Humidity Exchange +""""""""""""""""""""""""""""""""""""""""""""""""" +Near the surface, different processes will be appropriate for terrestrial vs. gaseous planets, and for different experimental designs. These are determined by namelist parameters in ``idealized_moist_phys`` as follows. + +**Terrestrial Planets** + +Isca will evaluate surface heat exchange provided a gaseous option is not specified (see below). Vertical diffusion may be enabled by setting the namelist parameter ``turb`` to ``True``; default is ``False``. This enables calls to ``vert_turb_driver_mod``, ``vert_diff_mod`` and ``mixed_layer_mod``. + +Additional namelist parameters further specify the processes called and parameters used: + ++----------------------------+-----------------------------------------------------------------------------+---------+ +| Option | Summary |Default | ++============================+=============================================================================+=========+ +|``mixed_layer_bc`` |Dictates whether ``mixed_layer_mod`` is called. |``False``| ++----------------------------+-----------------------------------------------------------------------------+---------+ +|``do_virtual`` |Selects whether virtual temperature is used in the vertical diffusion module.|``False``| ++----------------------------+-----------------------------------------------------------------------------+---------+ +|``roughness_moist`` |Roughness length for use in surface moisture exchange. |``0.05`` | ++----------------------------+-----------------------------------------------------------------------------+---------+ +|``roughness_mom`` |Roughness length for use in surface momentum exchange. |``0.05`` | ++----------------------------+-----------------------------------------------------------------------------+---------+ +|``roughness_heat`` |Roughness length for use in surface heat exchange. |``0.05`` | ++----------------------------+-----------------------------------------------------------------------------+---------+ +|``land_roughness_prefactor``|Multiplier on the above roughness lengths to allow land-ocean contrast. | ``1.0`` | ++----------------------------+-----------------------------------------------------------------------------+---------+ + + +**Gaseous Planets** + +Isca currently includes options to run a Jupiter-type planet. The relevant namelist parameter for the lower boundary physics in this case is: + ++----------------------------+------------------------------------------------------------------------------+---------+ +| Option | Summary |Default | ++============================+==============================================================================+=========+ +|``gp_surface`` |Turns on prescription of lower-boundary heat flux following [Schneider2009]_ .|``False``| ++----------------------------+------------------------------------------------------------------------------+---------+ + + +Upper Level Damping +""""""""""""""""""" + +At upper levels, damping may be needed to account for subgrid-scale processes that decelerate fast upper-level winds. The module ``damping_driver_mod`` applies Rayleigh friction at upper levels. This is switched on with: + ++----------------------------+-----------------------------------------------------------------------------+---------+ +| Option | Summary |Default | ++============================+=============================================================================+=========+ +|``do_damping`` |If true, call ``damping_driver_mod`` |``False``| ++----------------------------+-----------------------------------------------------------------------------+---------+ + +NB: ``damping_driver_mod`` is currently being configured to allow gravity wave drag options. + +Land and hydrology +^^^^^^^^^^^^^^^^^^ + +Land and hydrology processes are predominantly dealt with in ``surface_flux_mod`` and ``mixed_layer_mod``, but land and bucket hydrology options are initialised with the following namelist parameters. We acknowledge that the bucket hydrology is adapted from code from https://github.com/tapios and follows [Manabe1969]_. Land and hydrology options in this module are: + ++----------------------------+---------------------------------------------------------------------------------+-------------------+ +| Option | Summary |Default | ++============================+=================================================================================+===================+ +|``land_option`` |Selects how land-mask is defined, a summary of options is given below. |``none`` | ++----------------------------+---------------------------------------------------------------------------------+-------------------+ +|``land_file_name`` |Filename for the input land-mask. |``'INPUT/land.nc'``| ++----------------------------+---------------------------------------------------------------------------------+-------------------+ +|``land_field_name`` |Field name in the input land-mask netcdf. |``'land_mask'`` | ++----------------------------+---------------------------------------------------------------------------------+-------------------+ +|``bucket`` |If true, use bucket hydrology. |``False`` | ++----------------------------+---------------------------------------------------------------------------------+-------------------+ +|``init_bucket_depth`` |Value at which to initialise bucket water depth over ocean (large, in :math:`m`).|``1000.`` | ++----------------------------+---------------------------------------------------------------------------------+-------------------+ +|``init_bucket_depth_land`` |Value at which to initialise bucket water depth over land. |``20.`` | ++----------------------------+---------------------------------------------------------------------------------+-------------------+ +|``max_bucket_depth_land`` |Maximum depth of water in bucket over land following initialisation. |``0.15`` | ++----------------------------+---------------------------------------------------------------------------------+-------------------+ +|``robert_bucket`` |Robert coefficient for RAW filter* on bucket leapfrog timestepping. |``0.04`` | ++----------------------------+---------------------------------------------------------------------------------+-------------------+ +|``raw_bucket`` |RAW coefficient for RAW filter* on bucket leapfrog timestepping. |``0.53`` | ++----------------------------+---------------------------------------------------------------------------------+-------------------+ + +`*` Roberts-Asselin-Williams filter, [Williams2011]_ + +``land_option`` may be set to: + ++---------------+------------------------------------------------------------------------------------------+ +|Value | Effect | ++===============+==========================================================================================+ +|``'input'`` |Read land mask from input file. | ++---------------+------------------------------------------------------------------------------------------+ +|``'zsurf'`` |Define land where surface geopotential height at model initialisation exceeds a threshold.| ++---------------+------------------------------------------------------------------------------------------+ +|``'none'`` | Do not apply a land mask | ++---------------+------------------------------------------------------------------------------------------+ + + + + +Diagnostics +----------- + +Diagnostics from this module are output under ``mod_name = 'atmosphere'``. Some diagnostics may only be output when certain namelist options are set, e.g. those associated with the bucket hydrology. Requesting unsaved diagnostics in your diagnostic list will result in those diagnostics not being output, but will not cause a fatal error or affect other diagnostics. + + ++----------------------+-------------------------------------------------------------+------------------------------------+ +| Name | Description | Units | ++======================+=============================================================+====================================+ +|``dt_ug_diffusion`` | Zonal wind tendency from vertical diffusion | :math:`ms^{-2}` | ++----------------------+-------------------------------------------------------------+------------------------------------+ +|``dt_vg_diffusion`` | Meridional wind tendency from vertical diffusion | :math:`ms^{-2}` | ++----------------------+-------------------------------------------------------------+------------------------------------+ +|``dt_tg_diffusion`` | Temperature tendency from vertical diffusion | :math:`Ks^{-1}` | ++----------------------+-------------------------------------------------------------+------------------------------------+ +|``dt_qg_diffusion`` | Specific humidity tendency from vertical diffusion | :math:`kg kg^{-1} s^{-1}` | ++----------------------+-------------------------------------------------------------+------------------------------------+ +|``convection_rain`` | Rain from convection | :math:`kg m^{-2} s^{-1}` | ++----------------------+-------------------------------------------------------------+------------------------------------+ +|``condensation_rain`` | Rain from large-scale condensation | :math:`kg m^{-2} s^{-1}` | ++----------------------+-------------------------------------------------------------+------------------------------------+ +|``precipitation`` | Precipitation from resolved, parameterised and snow | :math:`kg m^{-2} s^{-1}` | ++----------------------+-------------------------------------------------------------+------------------------------------+ +|``dt_tg_convection`` | Temperature tendency from convection | :math:`Ks^{-1}` | ++----------------------+-------------------------------------------------------------+------------------------------------+ +|``dt_qg_convection`` | Specific humidity tendency from convection | :math:`kg kg^{-1} s^{-1}` | ++----------------------+-------------------------------------------------------------+------------------------------------+ +|``dt_tg_condensation``| Temperature tendency from condensation | :math:`Ks^{-1}` | ++----------------------+-------------------------------------------------------------+------------------------------------+ +|``dt_qg_condensation``| Specific humidity tendency from condensation | :math:`kg kg^{-1} s^{-1}` | ++----------------------+-------------------------------------------------------------+------------------------------------+ +|``rh`` | Relative humidity | % | ++----------------------+-------------------------------------------------------------+------------------------------------+ +|``cape`` | Convective Available Potential Energy | :math:`J kg^{-1}` | ++----------------------+-------------------------------------------------------------+------------------------------------+ +|``cin`` | Convective Inhibition | :math:`J kg^{-1}` | ++----------------------+-------------------------------------------------------------+------------------------------------+ +|``flux_u`` | Surface zonal wind stress | :math:`N m^{-2}` | ++----------------------+-------------------------------------------------------------+------------------------------------+ +|``flux_v`` | Surface meridional wind stress | :math:`N m^{-2}` | ++----------------------+-------------------------------------------------------------+------------------------------------+ +|``temp_2m`` | Air temperature 2m above surface | :math:`K` | ++----------------------+-------------------------------------------------------------+------------------------------------+ +|``sphum_2m`` | Specific humidity 2m above surface | :math:`kg kg^{-1}` | ++----------------------+-------------------------------------------------------------+------------------------------------+ +|``rh_2m`` | Relative humidity 2m above surface | % | ++----------------------+-------------------------------------------------------------+------------------------------------+ +|``u_10m`` | Zonal wind 10m above surface | :math:`ms^{-1}` | ++----------------------+-------------------------------------------------------------+------------------------------------+ +|``v_10m`` | Meridional wind 10m above surface | :math:`ms^{-1}` | ++----------------------+-------------------------------------------------------------+------------------------------------+ +|``diss_heat_ray`` | Heat dissipated by Rayleigh drag in [Schneider2009]_ scheme | :math:`Ks^{-1}` | ++----------------------+-------------------------------------------------------------+------------------------------------+ +|``bucket_depth`` | Depth of surface reservoir | :math:`m` | ++----------------------+-------------------------------------------------------------+------------------------------------+ +|``bucket_depth_conv`` | Tendency of bucket depth due to convection | :math:`ms^{-1}` | ++----------------------+-------------------------------------------------------------+------------------------------------+ +|``bucket_depth_cond`` | Tendency of bucket depth due to condensation | :math:`ms^{-1}` | ++----------------------+-------------------------------------------------------------+------------------------------------+ +|``bucket_depth_lh`` | Tendency of bucket depth due to evaporation | :math:`ms^{-1}` | ++----------------------+-------------------------------------------------------------+------------------------------------+ + + +Relevant modules and subroutines +-------------------------------- + +Key physics modules managed from this module include: + +* ``vert_turb_driver_mod`` +* ``vert_diff_mod`` +* ``two_stream_gray_rad_mod`` +* RRTM: see ``Isca/src/atmos_param/rrtm_radiation/`` +* SOCRATES: see ``Isca/src/atmos_param/socrates/`` +* ``mixed_layer_mod`` +* ``lscale_cond_mod`` +* ``qe_moist_convection_mod`` +* ``ras_mod`` +* ``betts_miller_mod`` +* ``dry_convection_mod`` +* ``surface_flux_mod`` +* ``damping_driver_mod`` +* ``rayleigh_bottom_drag_mod`` + +References +---------- + +[Betts1986]_ +[BettsMiller1986]_ +[Byrne2013]_ +[Frierson2006a]_ +[Frierson2007]_ +[Jucker2017]_ +[Manabe1969]_ +[MannersEtAl2015]_ +[MlawerEtAl1997]_ +[Moorthi1992]_ +[Schneider2006]_ +[Schneider2009]_ +[Williams2011]_ + +Authors +------- +This documentation was written by Ruth Geen, peer reviewed by Marianne Pietschnig, and quality controlled by Ross Castle. \ No newline at end of file diff --git a/docs/source/modules/index.rst b/docs/source/modules/index.rst new file mode 100644 index 000000000..4e59bb60e --- /dev/null +++ b/docs/source/modules/index.rst @@ -0,0 +1,24 @@ +.. _modules: + +################## +Components of Isca +################## + +.. toctree:: + :maxdepth: 1 + + introduction + dynamics + physics + constants + output + damping_driver + idealised_moist_phys + two_stream_gray_rad + convection_simple_betts_miller + lscale_cond + topography + socrates + mixedlayer + surface_flux + diag_manager_mod \ No newline at end of file diff --git a/docs/source/modules/introduction.rst b/docs/source/modules/introduction.rst new file mode 100644 index 000000000..0455e671b --- /dev/null +++ b/docs/source/modules/introduction.rst @@ -0,0 +1,115 @@ +Experiment configuration scripts +============== + +Summary +------- +Once you have managed to run a test case, you may want to run your own experiment. The model configuration, diagnostics, and simulation parameters +of your experiment can be configured with a python-based run script. +Find the test case in ``Isca/exp/test_cases`` that most resembles your experiment, copy and modify it. The important lines are described below. + +Setup +------- +:: + + from isca import DryCodeBase +imports the Isca python module that should have been installed. +``DryCodeBase`` refers to the part of the code needed to run the dry model. The codebase object is defined in ``Isca/src/extra/python/isca/codebase.py``. The different default codebases are designed to avoid the model compiling un-needed parts of the code, which speeds up compilation. ``DryCodeBase`` can be replaced by ``IscaCodeBase``, ``GreyCodeBase``, or ``SocratesCodeBase`` (provided you setup Socrates, see Socrates documentation). + +:: + + NCORES = 16 +specifies how many cores should be used when running the model. + +:: + + RESOLUTION = T42, 25 +specifies the horizontal (in this case, spectral T42) and vertical (25 pressure levels) +resolution. Runs at T42 by default. If the horizontal resolution is not T42, make sure the +input files (such as land masks) are changed and add ``exp.set_resolution(*RESOLUTION)`` after exp is defined. +Other common spectral resolutions are T21 and T85. + +:: + + cb = DryCodeBase.from_directory(GFDL_BASE) +tells Isca to find the model code at the location ``GFDL_BASE``, which is where the source code may be changed. You can replace ``from_directory(GFDL_BASE)`` with ``from_repo(repo='https://github.com/isca/isca', commit='isca1.1')`` to point to a specific git repository and commit id. This should ensure future, independent, reproducibility of results. But, the compilation depends on computer specific settings. The ``$GFDL_ENV`` environment variable is used to determine which ``$GFDL_BASE/src/extra/env`` file is used to load the correct compilers. The env file is always loaded from ``$GFDL_BASE`` and not the checked out git repo. + +:: + + cb.compile() +compiles the source code. + +:: + + exp = Experiment('EXPERIMENT_NAME', codebase=cb) +creates an experiment object which will handle the configuration of model parameters and output diagnostics. The output files will be found at ``$GFDL_DATA/EXPERIMENT_NAME``. + + +Diagnostics +------- + +:: + + diag = DiagTable() +creates a DiagTable object which we can configure to tell Isca which variables to output. + +:: + + diag.add_file('atmos_monthly', 30, 'days', time_units='days') +creates an ``atmos_monthly.nc`` file every 30 days, which is an average of the output over the previous 30 days. The output files can be found at ``$GFDL_DATA/EXPERIMENT_NAME/run####/*``. + +:: + + diag.add_field(MODULE_NAME, VARIABLE_NAME, time_avg=True) +determines which fields will be written in ``atmos_monthly.nc``. Find the available VARIABLE_NAMEs by going to the MODULE_NAME documentation or by finding the relevant source code (``cd Isca/src/ & find . -name "MODULE_NAME*"``). + + +Namelist +------- + +:: + + namelist = Namelist({...}) +defines a namelist object, which lets us configure the science options. +It is only necessary to set values that are different from the default parameters, which are defined +in the relevant module documentation (for example, ``atmosphere_nml`` parameters can be found in the ``atmosphere`` +module documentation or at the beginning of the ``atmosphere.F90`` source file). + +Running the experiment +------- + +:: + + exp.run(...) +will make the model run for the amount of time specified in ``main_nml`` (usually 30 days). + +The ``use_restart`` option can be set to ``False`` to start from scratch (isothermal atmosphere) or can point to a restart file (``use_restart = $GFDL_DATA/exp_name/run####/restarts/*``) to initialize the run from the output of a previous run. If unspecified, it will start from where the previous run left off or from an isothermal atmosphere in the absence of a previous run. + +Output +------- + +Output from the experiment can be found at ``$GFDL_DATA/EXP_NAME``. The atmospheric output is provided on +sigma levels where sigma is the pressure normalized by the surface pressure. For a planet with no topography, sigma and pressure levels are quite similar. +If there is topography present (such as in the ``realistic_continents`` test case), you need to interpolate the +data onto pressure levels before analyzing it. Top of atmosphere and surface values are not affected, but in-atmosphere values are. + +The details and code for interpolation to pressure levels can be found at https://github.com/ExeClim/Isca/tree/master/postprocessing/plevel_interpolation + +In the python code, there is a convenient function which can be used to call the interpolation code: https://github.com/ExeClim/Isca/blob/master/src/extra/python/isca/util.py (line 134). + +For example:: + + from isca.util import interpolate_output + for run in ["EXPERIMENT_NAME"]: + print(run) + for i in range(121, 241): + try: + infile = '/data_directory/' + run + '/run%04d/atmos_monthly.nc' % i + outfile = '/data_directory/' + run + '/run%04d/plev_monthly.nc' % i + interpolate_output(infile, outfile, p_levs='EVEN', var_names=['slp', 'height']) + except: + print(i) + +Authors +------- + +This documentation was written by Matthew Henry (heavily inspired from document written by Neil Lewis), peer reviewed by Will Seviour, and quality controlled by Brett McKim. diff --git a/docs/source/modules/lscale_cond.rst b/docs/source/modules/lscale_cond.rst new file mode 100644 index 000000000..f34943fad --- /dev/null +++ b/docs/source/modules/lscale_cond.rst @@ -0,0 +1,48 @@ +Large Scale Condensation and Precipitation +========================================== + +Summary +------- + +The module computes the large scale temperature and specific humidity adjustments needed in model layers where the relative humidity exceeds a threshold relative humidity, and returns the mass of rain and snow (or other frozen precipitation) that reaches the ground. The module also outputs the temperature tendency and the specific humidity tendency. Features include the option for the re-evaporation of falling precipitation and energetically consistent adjustment with precipitation type. + +See [Frierson2006a]_ (Section 2e) for a detailed description `here `_. + +Namelist options +---------------- + +There are three namelist variables. + +:hc: The relative humidity at which large scale condensation, where :math:`0.0 <= hc <= 1.0`. Default is :math:`hc=1.0`. +:do_evap: The flag for the re-evaporation of moisture in sub-saturated layers below, if ``True`` then re-evaporation is performed. Default is ``False``. +:do_simple: If ``True`` then all precipitation is rain/liquid precipitation, there is no snow/frozen precipitation. Default is ``False``. + +Diagnostics +----------- +The diagnostics are not sent out within the call to convection but are sent out from ``idealized_moist_phys.F90`` with the module name ``atmosphere``. The diagnostics relevant to the lscale_cond module are set out below: + ++-------------------+--------------------------------------+------------------------------+ +| Name | Description | Units | ++===================+======================================+==============================+ +| cond_dt_qg | Moisture tendency |:math:`kg~kg^{-1}~s^{-1}` | ++-------------------+--------------------------------------+------------------------------+ +| cond_dt_tg | Temperature tendency |:math:`K~s^{-1}` | ++-------------------+--------------------------------------+------------------------------+ +| cond_rain | Rain from condensation |:math:`kg~m^{-2}~s^{-1}` | ++-------------------+--------------------------------------+------------------------------+ +| precip | Rain and Snow from resolved and |:math:`kg~m^{-2}~s^{-1}` | +| | parameterised condensation/convection| | ++-------------------+--------------------------------------+------------------------------+ + +Relevant Modules and Subroutines +-------------------------------- +Relevant modules are: sat_vapor_pres_mod, utilities_mod, constants_mod. + +References +---------- + +The reference list within this docs page are: [Frierson2006a]_ + +Authors +------- +This documentation was written by Ross Castle and reviewed by Ruth Geen diff --git a/docs/source/modules/mixedlayer.rst b/docs/source/modules/mixedlayer.rst new file mode 100644 index 000000000..cec4398ef --- /dev/null +++ b/docs/source/modules/mixedlayer.rst @@ -0,0 +1,286 @@ +Mixed layer module +===================== + +Summary +---------------------- +This module updates the sea surface temperature (SST) noted as :math:`T_s` below. + +SST boundary condition options +----------------------- + +The SST options are: + - prescribe SST from an input file (``do_sc_sst`` namelist option). + - prescribe SST to follow an AquaPlanet Experiment protocol (APE) analytic form (``do_ape_sst`` namelist option). + - calculate SST based on the surface fluxes and mixed layer depth of a **slab ocean**, with the option of including a Q-flux (either analytic or read from a file). + +Note that only the final case will generate a closed surface energy budget. Each of these options are discussed below, followed by an outline of the implicit time-stepping process used when SST is not prescribed. + +Input SST file +----------------------- +Set ``do_sc_sst`` to True if you want to specify the SST field. The SST field will be read in from a NetCDF file with a file name specified by the ``sst_file`` variable. +Using an input SST field is useful, for example, when you want to add a temperature anomaly. The file is read in during the initialisation (i.e. within the call to ``mixed_layer_init`` from within ``idealized_moist_phys``). +The input file can be time independent (i.e. no diurnal or seasonal cycle or any changes in the SST from one time step to another) or vary with time. More information can be found in the Diagnostics section below. + +APE aquaplanet (analytic SST) +----------------------- +The prescribed SST for the APE aquaplanet protocol is given by: + +.. math:: + T_s = 27 \left( 1 - \sin^2\left( \frac{3}{2} \lambda \right) \right), + +between 60N-60S, equation 1 of Neele and Hoskins 2004 [NealeHoskins2004]_, and 0 deg C poleward of 60N/S. + +Slab ocean +----------------------- +To allow the SST to evolve based on the surface fluxes, the atmosphere can be coupled to a slab ocean, whose depth is specified by the namelist parameter ``depth``. This also allows a closed surface energy budget, useful for e.g. simulations with increased greenhouse gases. + +In this case, during the initialisation, if there is no restart file to open, the surface temperature is set to the prescribed initial distribution (``prescribe_initial_dist = True``): + +.. math:: + T_s = T_{surf} -\frac{1}{3} dT \left(3\sin(\lambda)^2-1\right), + +where the default values we use in the trip test for the Frierson test case (``exp/test_cases/frierson/frierson_test_case.py``) are: :math:`T_{surf} = 285 K` and :math:`dT = 40 K`. + +This form of :math:`T_{surf}` is similar to a 2nd legendre polynomial, it is a parabola that maximises at the equator. + +Implicit timestepping procedure +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +The mixed layer module calculates the evolution of surface temperature using an implicit timestep. +Whereas an explicit method uses the current state of the system to calculate the state of the system +at the next timestep, an implicit method uses the inferred state of the system at the next timestep. + +The net flux into the surface is given by:: + + net_surf_sw_down + surf_lw_down - flux_r - SH - LH + ocean_qflux + +where ``net_surf_sw_down`` is the net shortwave radiation into the surface, ``surf_lw_down`` the downwelling longwave radiation into the surface, and ``flux_r`` the upwelling longwave radiation at the +surface. The sensible heat (``SH``) is the sum of the surface sensible heat flux and the temperature diffusion flux. The latent heat (``LH``) is the sum of the surface latent heat and the +moisture diffusion flux (only included if ``evaporation`` is true). The ``ocean_flux`` is an optional term and is zero if the Q-flux is not used. + +If ``do_calc_eff_heat_cap`` is True, the ``land_sea_heat_capacity`` is used at each timestep to compute the surface temperature using the following equation:: + + land_sea_heat_capacity * dTs/dt = - corrected_flux - t_surf_dependence * dTs + +The change in surface temperature in this timestep (``dTs/dt``) times the heat capacity of the surface is equal to the net flux into the surface at the given timestep (``- corrected_flux``) plus the change in the net flux +into the surface caused by the change in surface temperature (``- t_surf_dependence * dTs``). + +This is simplified by defining ``eff_heat_capacity`` as ``land_sea_heat_capacity + t_surf_dependence * dt``, and then ``Ts`` is updated using:: + + eff_heat_capacity * dTs/dt = - corrected_flux + +Optional Q-flux +^^^^^^^^^^^^^^^ +The slab ocean model only communicates between grid-boxes in the vertical (i.e. air-sea exchange) but does not represent any horizontal transport (i.e. no north-south or east-west communications between grid cells). +An idealised horizontal transport can be included using an ocean heat flux (Q-flux). Atmospheric heat transport is more realistic with an ocean heat transport. + +Isca is able to calculate an analytic Q-flux, appropriate to an aquaplanet, following Merlis et al 2013 [MerlisEtAl2013] if ``do_qflux`` is True. Additionally, an analytic warmpool can be added by setting ``do_warmpool``. The warmpool structure is set in ``Isca/src/atmos_param/qflux/qflux.f90``. Alternatively an arbitrary Q-flux may be read from a file if ``load_qflux`` is True. + +In the case where both a specific SST distribution (e.g. AMIP climatology) and closed surface energy budget are desired, a Q-flux input file can be generated by running a control experiment with the prescribed SST, creating a NetCDF Q-flux file offline and then passing this file to the model via the python interface run script. + +1. Run a prescribed experiment (i.e. a control) using either observations, AMIP or similar. + +2. Using the prescribed SST field and the surface fluxes from step 1, create the Q-flux file. This is an offline script that is run independently of the model. An example script is shown in: ``src/extra/python/scripts/calculate_qflux/calculate_qflux.py`` but you can create your own script to do this depending on your application. + +3. Add the Q-Flux file to the ``inputfiles`` in the python run script (same as you would for ozone, land etc). Then in the ``mixed_layer_nml`` namelist in the python run script set ``load_qflux`` to True, ``qflux_file_name`` to the name of the input file (don't include the .nc extension) and ``qflux_field_name`` is the Q-flux variable name in the file. + + +See Q-flux options below for namelist options. Note that the Q-flux is only relevant for slab ocean experiments (not fixed or prescribed SST runs). Also note that if the MiMA radiation code is used then the Q-flux is implemented following Merlis et al 2013 [MerlisEtAl2013]_ + +More information on the method for Q-flux can be found in Russel et al 1985 [RusselEtAl1985]_ + + +Namelist options +---------------- + ++-------------------+------------------------------------------------------------+---------+ +| Option | Summary |Default | ++===================+============================================================+=========+ +|``evaporation`` |Switch for surface evaporation. |``True`` | ++-------------------+------------------------------------------------------------+---------+ +|``depth`` |Mixed layer depth. | ``40.0``| ++-------------------+------------------------------------------------------------+---------+ + +Q-flux options +^^^^^^^^^^^^^^^^^^^^ +If ``do_qflux`` is True, use ``qflux_amp`` and ``qflux_width`` to calculate a time-independent surface Q-flux. + ++-------------------+----------------------------------------------------------------+---------+ +| Option | Summary |Default | ++===================+================================================================+=========+ +|``do_qflux`` | Switch to calculate time-independent Q-flux. |``False``| ++-------------------+----------------------------------------------------------------+---------+ +|``qflux_amp`` | Amplitude of time-independent Q-flux if ``do_qflux`` is True. | ``0.0`` | ++-------------------+----------------------------------------------------------------+---------+ +|``qflux_width`` | Width of time-independent Q-flux if ``do_qflux`` is True. | ``16.0``| ++-------------------+----------------------------------------------------------------+---------+ + +If ``load_qflux`` is True, use input file to load in a time-independent or time-dependent Q-flux. + ++----------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+ +| Option | Summary |Default | ++======================+=============================================================================================================================================================================+=================+ +|``load_qflux`` | Switch to use input file to get Q-flux. | ``False`` | ++----------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+ +|``qflux_file_name`` | Name of file among input files, from which to get Q-flux. | ``ocean_qflux`` | ++----------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+ +|``qflux_field_name`` | Name of field name in Q-flux file name, from which to get Q-flux. This is only used when ``time_varying_qflux`` is False. Otherwise the code assumes field_name = file_name.| ``ocean_qflux`` | ++----------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+ +|``time_varying_qflux``| Flag that determines whether input Q-flux file is time dependent. | ``False`` | ++----------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+ + +Initialize surface temperature +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ++-------------------------------+----------------------------------------------------------------------------------+-----------+ +| Option | Summary |Default | ++===============================+==================================================================================+===========+ +|``prescribe_initial_dist`` | Switch to turn on setting the initial surface temperature distribution. | ``305.0`` | ++-------------------------------+----------------------------------------------------------------------------------+-----------+ +|``tconst`` | Initial surface temperature following formula in ``Slab ocean`` section. | ``305.0`` | ++-------------------------------+----------------------------------------------------------------------------------+-----------+ +|``delta_T`` | Initial surface temperature gradient following formula in ``Slab ocean`` section.| ``40.0`` | ++-------------------------------+----------------------------------------------------------------------------------+-----------+ +|``do_read_sst`` | Flag to use fixed SSTs, prescribed from input file (``sst_file``). | ``False`` | ++-------------------------------+----------------------------------------------------------------------------------+-----------+ +|``sst_file`` | Name of file containing fixed SSTs. | ``False`` | ++-------------------------------+----------------------------------------------------------------------------------+-----------+ +|``do_sc_sst`` | Flag to use fixed SSTs, prescribed from input file (``sst_file``). | ``False`` | ++-------------------------------+----------------------------------------------------------------------------------+-----------+ +|``specify_sst_over_ocean_only``| Flag to specify SSTs only over ocean, only works if ``do_sc_sst`` is True. | ``False`` | ++-------------------------------+----------------------------------------------------------------------------------+-----------+ +|``do_ape_sst`` | Flag to set prescribed SST according to the APE aquaplanet analytic form | ``False`` | ++-------------------------------+----------------------------------------------------------------------------------+-----------+ +|``add_latent_heat_flux_anom`` | Flag to add an anomalous latent heat flux | ``False`` | ++-------------------------------+----------------------------------------------------------------------------------+-----------+ +|``do_warmpool`` | Flag to call warmpool module, which returns ``ocean_qflux``. | ``False`` | ++-------------------------------+----------------------------------------------------------------------------------+-----------+ + +Surface albedo options +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +There are 5 options for setting the surface albedo, determined by the value of ``albedo_choice``. + - 1: Surface albedo is a constant (``albedo_value``). + - 2: Glacier with higher albedo in one hemisphere only. If ``lat_glacier`` >0, albedo = ``higher_albedo`` North of ``lat_glacier``. If ``lat_glacier`` <0, albedo = ``higher_albedo`` South of ``lat_glacier``. + - 3: Glacier with higher albedo in both hemispheres. Albedo = ``higher_albedo`` where latitude > ``|lat_glacier|``. + - 4: Albedo set by ``albedo_value + (higher_albedo - albedo_value) (lat/90) ^ albedo_exp``. + - 5: Tanh increase around ``albedo_cntr`` with ``albedo_wdth``:: + + albedo(lat) = albedo_value + (higher_albedo-albedo_value)* 0.5 *(1+tanh((lat-albedo_cntr)/albedo_wdth)). + ++-------------------+-----------------------------------------------------------------------------+---------+ +| Option | Summary |Default | ++===================+=============================================================================+=========+ +|``albedo_choice`` | Switch to choose surface albedo option described above. | ``1`` | ++-------------------+-----------------------------------------------------------------------------+---------+ +|``albedo_value`` | Parameter that sets surface albedo depending on albedo choice. | ``0.06``| ++-------------------+-----------------------------------------------------------------------------+---------+ +|``higher_albedo`` | Parameter that sets surface albedo depending on albedo choice. | ``0.10``| ++-------------------+-----------------------------------------------------------------------------+---------+ +|``lat_glacier`` | Parameter that sets the glacier latitude for albedo choices 2 and 3. | ``60.0``| ++-------------------+-----------------------------------------------------------------------------+---------+ +|``albedo_exp`` | Parameter that sets surface albedo latitude dependence for albedo choice 4. | ``2.`` | ++-------------------+-----------------------------------------------------------------------------+---------+ +|``albedo_cntr`` | Parameter that sets surface albedo for albedo choice 5. | ``45.0``| ++-------------------+-----------------------------------------------------------------------------+---------+ +|``albedo_wdth`` | Parameter that sets surface albedo for albedo choice 5. | ``10`` | ++-------------------+-----------------------------------------------------------------------------+---------+ + +Land options +^^^^^^^^^^^^^^^^ + +There are 4 options for setting up the land, determined by the value of ``land_option``. + - ``none``: No land. + - ``input``: Use input file to determine land mask. + - ``zsurf``: The surface heat capacity is set to ``land_capacity`` where the surface geopotential is greater than 10. + - ``lonlat``: The surface heat capacity is set to ``land_capacity`` in the longitude / latitude boxes set by [slandlon(k), elandlon(k)] and [slandlat(k), elandlat(k)] for all k's. + ++------------------------------+---------------------------------------------------------------------------------------------------------+----------+ +| Option | Summary | Default | ++==============================+=========================================================================================================+==========+ +|``land_option`` | Switch to choose land option as described above. | ``none`` | ++------------------------------+---------------------------------------------------------------------------------------------------------+----------+ +|``land_depth`` | Value of land mixed layer depth. | ``-1`` | ++------------------------------+---------------------------------------------------------------------------------------------------------+----------+ +|``slandlon`` | Vector determining lower bounds of longitudes for land masses. | ``0`` | ++------------------------------+---------------------------------------------------------------------------------------------------------+----------+ +|``slandlat`` | Vector determining lower bounds of latitudes for land masses. | ``0`` | ++------------------------------+---------------------------------------------------------------------------------------------------------+----------+ +|``elandlon`` | Vector determining higher bounds of longitudes for land masses. | ``-1`` | ++------------------------------+---------------------------------------------------------------------------------------------------------+----------+ +|``elandlat`` | Vector determining higher bounds of latitudes for land masses. | ``-1`` | ++------------------------------+---------------------------------------------------------------------------------------------------------+----------+ +|``land_h_capacity_prefactor`` | Factor by which to multiply ocean heat capacity to get land heat capacity if ``input`` option is used. | ``1.0`` | ++------------------------------+---------------------------------------------------------------------------------------------------------+----------+ +|``land_albedo_prefactor`` | Factor by which to multiply ocean albedo to get land albedo if ``input`` option is used. | ``1.0`` | ++------------------------------+---------------------------------------------------------------------------------------------------------+----------+ + +Ice options +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ++-------------------------------+-------------------------------------------------------------------------------------------------------------+----------------------+ +| Option | Summary |Default | ++===============================+=============================================================================================================+======================+ +|``update_albedo_from_ice`` | Flag to set the surface albedo to ``ice_albedo_value`` where there is ice as specified by ``ice_file_name`` | ``False`` | ++-------------------------------+-------------------------------------------------------------------------------------------------------------+----------------------+ +|``ice_albedo_value`` | Value for ice albedo when ``update_albedo_from_ice`` is True. | ``0.7`` | ++-------------------------------+-------------------------------------------------------------------------------------------------------------+----------------------+ +|``ice_file_name`` | Name of file containing sea ice concentration. | ``siconc_clim_amip`` | ++-------------------------------+-------------------------------------------------------------------------------------------------------------+----------------------+ +|``ice_concentration_threshold``| Value of sea ice concentration above which albedo should be set to ``ice_albedo_value``. | ``0.5`` | ++-------------------------------+-------------------------------------------------------------------------------------------------------------+----------------------+ +|``ice_file_name`` | Name of file containing sea ice concentration. | ``siconc_clim_amip`` | ++-------------------------------+-------------------------------------------------------------------------------------------------------------+----------------------+ + +Diagnostics +------------------- ++---------------------------+-------------------------------------+----------------------------------------------+ +| Name | Description | Units | ++===========================+=====================================+==============================================+ +| ``t_surf`` | Surface temperature | K | ++---------------------------+-------------------------------------+----------------------------------------------+ +| ``delta_t_surf`` | Surface temperature change | K | ++---------------------------+-------------------------------------+----------------------------------------------+ +| ``flux_t`` | Surface sensible heat flux | :math:`\text{W}\,\text{m}^{-2}` | ++---------------------------+-------------------------------------+----------------------------------------------+ +| ``flux_lhe`` | Surface latent heat flux | :math:`\text{W}\,\text{m}^{-2}` | ++---------------------------+-------------------------------------+----------------------------------------------+ +| ``flux_oceanq`` | Ocean heat flux | :math:`\text{W}\,\text{m}^{-2}` | ++---------------------------+-------------------------------------+----------------------------------------------+ +| ``ice_conc`` | Sea ice concentration | 0-1 | ++---------------------------+-------------------------------------+----------------------------------------------+ +| ``albedo`` | Surface albedo | 0-1 | ++---------------------------+-------------------------------------+----------------------------------------------+ +| ``land_sea_heat_capacity``| Heat capacity of land and sea | :math:`\text{J}\,\text{m}^{-2},\text{K}^{-1}`| ++---------------------------+-------------------------------------+----------------------------------------------+ + + +Relevant modules and subroutines +-------------------------------- +.. List the names of relevant modules, subroutines, functions, etc. +.. You can add also code snippets, using Sphinx code formatting + +The mixed layer code is located in: ``src/atmos_spectral/driver/solo/mixed_layer.F90``. The name of this file reflects the fact that the code determines the properties of the single layer (either a slab ocean model +or prescribed SST) below the air-sea interface. + +The mixed layer ocean is initialised and called by: ``src/atmos_spectral/driver/solo/idealized_moist_phys.F90``. + +Relevant routines which are called by the mixed layer ocean: + - The SST input file is read in using the interpolator module found here: ``src/atmos_shared/interpolator/interpolator.F90``. + - The Q-flux and warmpool components use the Q-flux module: ``src/atmos_param/qflux/qflux.f90``. + +References +---------- +.. + Add relevant references. This is done in 2 steps: + 1. Add the reference itself to docs/source/references.rst + 2. Insert the citation key here, e.g. [Vallis2017]_ + + See the Contributing guide for more info. +[Vallis2017]_ +[NealeHoskins2004]_ +[MerlisEtAl2013]_ +[RusselEtAl1985]_ + +Authors +---------- +.. +This documentation was written by Matthew Henry and Penelope Maher, peer reviewed by Stephen Thomson, and quality controlled by Ruth Geen. diff --git a/docs/source/modules/output.rst b/docs/source/modules/output.rst new file mode 100644 index 000000000..764d84fd5 --- /dev/null +++ b/docs/source/modules/output.rst @@ -0,0 +1,4 @@ +Changing Isca output +==================== + +Coming soon... diff --git a/docs/source/modules/physics.rst b/docs/source/modules/physics.rst new file mode 100644 index 000000000..6ce8f4227 --- /dev/null +++ b/docs/source/modules/physics.rst @@ -0,0 +1,4 @@ +Isca's physical parameterisations +================================= + +Coming soon... diff --git a/docs/source/modules/socrates.rst b/docs/source/modules/socrates.rst new file mode 100644 index 000000000..258b92505 --- /dev/null +++ b/docs/source/modules/socrates.rst @@ -0,0 +1,215 @@ + +SOCRATES Radiation Scheme Interface +=================================== + +Summary +------- +.. This summary is modified from Stephen Thomson's PR for Socrates: + +SOCRATES (Suite Of Community RAdiative Transfer codes based on Edwards and Slingo) is the radiation scheme used by UK Met Office for Earth and planetary science [MannersEtAl2015]_, which has many significant advantages over RRTM, notably its flexibility in terms of atmospheric composition and the spectral properties of the radiation scheme (e.g. number of bands, etc). + +* The code used to integrate Socrates into Isca is contained within the folder ``src/atmos_params/socrates/interface``. +* The Socrates source code itself is **NOT** packed within this Isca repository, and **NEW** users will need to download it from the `Met Office Science Repository `_. Users can then either choose to put the Socrates code within the directory ``src/atmos_params/socrates/src/trunk``, or can set the bash environment variable ``GFDL_SOC`` equal to the location of the source code for Socrates. Detailed instructions on how to do this are included in the `README.md `_ for the Socrates test-case: ``exp/test_cases/socrates_test/README.md``. +* The basis of ``socrates_interface`` was coded by Mark Hammond (Univ. of Oxford) and James Manners (Met Office) and modified by Stephen Thomson (Univ. of Exeter) [Thomson_and_Vallis2019]_. Features added include seasonality in the radiation based on Isca's ``astronomy`` package, and the ability to use a ``radiation timestep != atmospheric timestep``. +* Socrates radiation scheme requires ``mass mixing ratios`` for all quantities (e.g. CO2, water vapor etc). This contrasts with RRTM, which wants ``volume mixing ratios``. + + +Namelist options +---------------- + +The Socrates namelist ``socrates_rad_nml`` can be found at ``src/atmos_param/socrates/interface/socrates_config_mod.f90``. + +Radiation options +^^^^^^^^^^^^^^^^^ + +Here are some options to set incoming radiation: + ++----------------------------+----------+-----------------------------------------------------------------------------------------+ +| Name | Default | Description | ++============================+==========+=========================================================================================+ +|``solday`` | 0 | If ``solday>0``, do perpetual run corresponding to day of the year = solday in | +| | | [0, days per year] | ++----------------------------+----------+-----------------------------------------------------------------------------------------+ +|``do_rad_time_avg`` | True | Average ``coszen`` for shortwave radiation over ``dt_rad`` | ++----------------------------+----------+-----------------------------------------------------------------------------------------+ +|``equinox_day`` | 0.75 | Fraction of the year defining NH autumn equinox in ``[0, 1]`` | ++----------------------------+----------+-----------------------------------------------------------------------------------------+ +|``stellar_constant`` | 1368.22 | solar constant (units: Wm :math:`^{-2}`), consistent with RRTM default | ++----------------------------+----------+-----------------------------------------------------------------------------------------+ +|``tidally_locked`` | False | Tidally locked or not | ++----------------------------+----------+-----------------------------------------------------------------------------------------+ +|``frierson_solar_rad`` | False | Options for annual-mean incoming solar, as prescribed in Frierson's grey scheme | ++----------------------------+----------+-----------------------------------------------------------------------------------------+ +|``del_sol`` | 1.4 | Latitudinal variation of shortwave radiation, as prescribed in Frierson's grey scheme | ++----------------------------+----------+-----------------------------------------------------------------------------------------+ +|``del_sw`` | 0 | Latitudinal variation of shortwave radiation, as prescribed in Frierson's grey scheme | ++----------------------------+----------+-----------------------------------------------------------------------------------------+ +|``input_planet_emissivity`` | 1.0 | Emissivity of surface. Defined as constant all over surface. | ++----------------------------+----------+-----------------------------------------------------------------------------------------+ + +The following namelist variables set radiation time stepping and spatial sampling: + ++----------------------------+----------+---------------------------------------------------------------------------------------------------------+ +| Name | Default | Description | ++============================+==========+=========================================================================================================+ +| ``dt_rad`` | 0 | Radiation timestep - every step if ``dt_rad`_) | ++----------------------------+----------+---------------------------------------------------------------------------------------------------------+ + +Spectral files +^^^^^^^^^^^^^^ + +Socrates reads external input spectral files that tell it the number of spectral bands to use, with one file setting the shortwave options, and another file setting the longwave options. Some spectral files have lots of bands, which will make the model run slowly. The default files used in the Met Office's Unified Model-GA7, and also in Isca, can be found within the ``data/spectra`` directory of Socrates source code. For example, it can be here if you put the Socrates source code within the ``trunk`` directory: +:: + src/atmos_param/socrates/src/trunk/data/spectra/ga7/sp_lw_ga7 for the longwave + src/atmos_param/socrates/src/trunk/data/spectra/ga7/sp_sw_ga7 for the shortwave + +or here if you have set ``GFDL_SOC`` as an environment variable: +:: + $GFDL_SOC/data/spectra/ga7/sp_lw_ga7 for the longwave + $GFDL_SOC/data/spectra/ga7/sp_sw_ga7 for the shortwave + ++--------------------------------+----------+----------------------------------------------------------------------------+ +| Name | Default | Description | ++================================+==========+============================================================================+ +| ``socrates_hires_mode`` | False | If ``False`` then run in 'GCM mode', and | +| | | if ``True`` then use high-res spectral file | ++--------------------------------+----------+----------------------------------------------------------------------------+ +| ``lw_spectral_filename`` | 'unset' | Longwave spectral file, which can be found at Socrates source code | +| | | ``data`` directory (e.g. ``data/spectra/ga7/sp_lw_ga7``) or self-generated | ++--------------------------------+----------+----------------------------------------------------------------------------+ +| ``sw_spectral_filename`` | 'unset' | Shortwave spectral file, which can be found at Socrates source code | +| | | ``data`` directory (e.g. ``data/spectra/ga7/sp_sw_ga7``) or self-generated | ++--------------------------------+----------+----------------------------------------------------------------------------+ +| ``lw_hires_spectral_filename`` | 'unset' | High-res longwave spectral file if ``socrates_hires_mode`` is ``True`` | ++--------------------------------+----------+----------------------------------------------------------------------------+ +| ``sw_hires_spectral_filename`` | 'unset' | High-res shortwave spectral file if ``socrates_hires_mode`` is ``True`` | ++--------------------------------+----------+----------------------------------------------------------------------------+ + + +CO2, ozone and other gases +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +To include radiative effects of water vapor, CO2 and ozone, the following switches should be ``True``: + ++-------------+---------------+-----------------------------------------------------+ +| Name | Default | Description | ++=============+===============+=====================================================+ +| ``inc_h2o`` | True | To include radiative effects of water vapor | ++-------------+---------------+-----------------------------------------------------+ +| ``inc_co2`` | True | To include radiative effects of CO2 | ++-------------+---------------+-----------------------------------------------------+ +| ``inc_o3`` | True | To include radiative effects of ozone | ++-------------+---------------+-----------------------------------------------------+ + +In addition, you need to set their concentrations by specifing them directly or reading from the external files (close attention should be paid to the concentration units): + ++-----------------------------------+---------------+-----------------------------------------------------------------------------+ +| Name | Default | Description | ++===================================+===============+=============================================================================+ +| ``account_for_effect_of_water`` | True | - ``False``: radiation is fed water mixing ratios = 0 | +| | | - ``True``: radiation is fed mixing ratios based on model specific humidity | ++-----------------------------------+---------------+-----------------------------------------------------------------------------+ +| ``account_for_effect_of_ozone`` | True | - ``False``: radiation is fed ozone mixing ratios = 0 | +| | | - ``True``: radiation is fed mixing ratios based on model ozone field | ++-----------------------------------+---------------+-----------------------------------------------------------------------------+ +| ``do_read_co2`` | False | - Read CO2 from an external file? | +| | | - If ``True``, needs to specify CO2 file and variable names | ++-----------------------------------+---------------+-----------------------------------------------------------------------------+ +| ``co2_file_name`` | 'co2' | Name of file containing CO2 field - n.b. don't need to include ``'.nc'`` | ++-----------------------------------+---------------+-----------------------------------------------------------------------------+ +| ``co2_field_name`` | 'co2' | Name of CO2 variable in CO2 file (specified by ``co2_file_name``) | ++-----------------------------------+---------------+-----------------------------------------------------------------------------+ +| ``input_co2_mmr`` | False | - ``True`` if the input file contain values as ``mass mixing ratio`` | +| | | - ``False`` if the input file contain values as ``volume mixing ratio`` | ++-----------------------------------+---------------+-----------------------------------------------------------------------------+ +| ``co2_ppmv`` | 300 | Default CO2 concentration in ``ppmv`` | ++-----------------------------------+---------------+-----------------------------------------------------------------------------+ +| ``do_read_ozone`` | False | - Read ozone from an external file? | +| | | - If ``True``, needs to specify ozone file and variable names | ++-----------------------------------+---------------+-----------------------------------------------------------------------------+ +| ``ozone_file_name`` | 'ozone' | Name of file containing ozone field - n.b. don't need to include ``'.nc'`` | ++-----------------------------------+---------------+-----------------------------------------------------------------------------+ +| ``ozone_field_name`` | 'ozone' | Name of ozone variable in ozone file (specified by ``ozone_file_name``) | ++-----------------------------------+---------------+-----------------------------------------------------------------------------+ +| ``input_o3_file_is_mmr`` | True | - ``True`` if the input file contain values as ``mass mixing ratio`` | +| | | - ``False`` if the input file contain values as ``volume mixing ratio`` | ++-----------------------------------+---------------+-----------------------------------------------------------------------------+ + +To include the radiative effects of other gases, such as CO, CH4, O2, SO2, CFC, etc, first you need to turn on the switches starting with ``inc_`` (default ``False``), then specify the corresponding concentrations through variables ending with ``_mix_ratio`` in the namelist. + + +Diagnostics +----------- + +Diagnostics from Socrates are under module name ``socrates``. The outputs include the temperature tendencies due to LW/SW radiation, LW/SW radiation fluxes at each level, and the fluxes at surface and the top of the atmosphere (TOA). + ++--------------------------+-----------------------------------------------------+---------------------+--------------------------------+ +| Name | Description | Units | Dimension (not including time) | ++==========================+=====================================================+=====================+================================+ +|``soc_tdt_lw`` | Socrates temperature tendency due to LW radiation | Ks :math:`^{-1}` | (pfull, lat, lon) | ++--------------------------+-----------------------------------------------------+---------------------+--------------------------------+ +|``soc_tdt_sw`` | Socrates temperature tendency due to SW radiation | Ks :math:`^{-1}` | (pfull, lat, lon) | ++--------------------------+-----------------------------------------------------+---------------------+--------------------------------+ +|``soc_tdt_rad`` | Socrates temperature tendency due to radiation | Ks :math:`^{-1}` | (pfull, lat, lon) | ++--------------------------+-----------------------------------------------------+---------------------+--------------------------------+ +|``soc_flux_lw`` | Socrates net LW flux (positive up) | Wm :math:`^{-2}` | (phalf, lat, lon) | ++--------------------------+-----------------------------------------------------+---------------------+--------------------------------+ +|``soc_flux_sw`` | Socrates net SW flux (positive up) | Wm :math:`^{-2}` | (phalf, lat, lon) | ++--------------------------+-----------------------------------------------------+---------------------+--------------------------------+ +|``soc_surf_flux_lw`` | Socrates net LW surface flux (positive up) | Wm :math:`^{-2}` | (lat, lon) | ++--------------------------+-----------------------------------------------------+---------------------+--------------------------------+ +|``soc_surf_flux_lw_down`` | Socrates LW surface flux down | Wm :math:`^{-2}` | (lat, lon) | ++--------------------------+-----------------------------------------------------+---------------------+--------------------------------+ +|``soc_surf_flux_sw`` | Socrates net SW surface flux (positive down) | Wm :math:`^{-2}` | (lat, lon) | ++--------------------------+-----------------------------------------------------+---------------------+--------------------------------+ +|``soc_surf_flux_sw_down`` | Socrates SW surface flux down | Wm :math:`^{-2}` | (lat, lon) | ++--------------------------+-----------------------------------------------------+---------------------+--------------------------------+ +|``soc_olr`` | Socrates TOA LW flux (positive up) | Wm :math:`^{-2}` | (lat, lon) | ++--------------------------+-----------------------------------------------------+---------------------+--------------------------------+ +|``soc_toa_sw`` | Socrates net TOA SW flux (positive down) | Wm :math:`^{-2}` | (lat, lon) | ++--------------------------+-----------------------------------------------------+---------------------+--------------------------------+ +|``soc_toa_sw_down`` | Socrates net TOA SW flux down | Wm :math:`^{-2}` | (lat, lon) | ++--------------------------+-----------------------------------------------------+---------------------+--------------------------------+ +|``soc_coszen`` | Socrates cosine (zenith_angle) | None | (lat, lon) | ++--------------------------+-----------------------------------------------------+---------------------+--------------------------------+ +|``soc_co2`` | Socrates CO2 concentration (mass mixing ratio) | kg kg :math:`^{-1}` | (pfull, lat, lon) | ++--------------------------+-----------------------------------------------------+---------------------+--------------------------------+ +|``soc_ozone`` | Socrates ozone concentration (mass mixing ratio) | kg kg :math:`^{-1}` | (pfull, lat, lon) | ++--------------------------+-----------------------------------------------------+---------------------+--------------------------------+ +|``soc_spectral_olr`` | Socrates substellar OLR spectrum | Wm :math:`^{-2}` | (socrates_lw_bins, lat, lon) | ++--------------------------+-----------------------------------------------------+---------------------+--------------------------------+ + + +Relevant modules and subroutines +-------------------------------- + +The Socrates radiation scheme is initiatized and called by ``src/atmos_spectral/driver/solo/idealized_moist_phys.F90``. + +The major modules/files under ``src/atmos_param/socrates/interface/`` are: + +* ``socrates_interface.F90`` and ``socrates_calc.F90``: The Socrates interfaces that initialize/finalize the Socrates, call subroutines to get inputs, set options for radiation and run core radiaiton code, and output the diagnostics. +* ``socrates_config_mod.f90``: module to set the namelist, including the solar radiation options, time-step, and concentrations of CO2, ozone and other well-mixed gases +* ``read_control.F90`` and ``set_control.F90``: The Socrates use the ``StrCtrl`` structure to control the switches for core radiaiton code. For example, if you want to include the effects of CO2, you not only need to provide the value of CO2 concentration, but also need to turn on the switch to tell Socrates to calculate its effect: set ``control%l_co2 = .true.``, where ``control`` is a ``StrCtrl`` structure. Basically, all the logical switches are set in these two files. +* ``set_bound.F90`` and ``set_dimen.F90``: modules to set the boundary fields and dimensions for the radiation code +* ``set_atm.F90``, ``set_aer.F90``, and ``set_cld.F90``: set the input atmospheric profiles, aerosol fields and clouds fields for the core radiation code (currently the aerosol and clouds are not activated) + +Other radiation schemes employed in Isca can be found at: + +* RRTM: see ``src/atmos_param/rrtm_radiation`` +* Two-stream gray radiation: see ``src/atmos_param/two_stream_gray_rad`` + +References +---------- +[MannersEtAl2015]_ +[Thomson_and_Vallis2019]_ + +Authors +------- +This documentation was written by Qun Liu (thanks to Stephen's PR of Socrates), peer reviewed by Stephen Thomson, and quality controlled by Ruth Geen. diff --git a/docs/source/modules/surface_flux.rst b/docs/source/modules/surface_flux.rst new file mode 100644 index 000000000..38ff266c1 --- /dev/null +++ b/docs/source/modules/surface_flux.rst @@ -0,0 +1,170 @@ + +Surface flux calculation +======================================================================================= + +Documentation for ``surface_flux`` and ``gp_surface_flux``, both found at ``Isca/src/coupler/surface_flux.F90``. + + +Summary +------- + +This module manages the exchange of heat, momentum and moisture at the planet's surface. +A brief overview of how the exchange of these is calculated and the options available is given first, with a full summary of the namelist parameters below. + + +Parametrization for planets with a rock/liquid surface +------------------------------------------------------ +Fluxes of heat (:math:`S`), momentum (:math:`\mathcal{T}`) and humidity (:math:`E`) are calculated following the standard drag equations set out in [Frierson2006a]. + +.. math:: + S = \rho_{a} c_{p} C_{S} |\mathbf{v}_{a}|(\Theta_{a} - \Theta_{s}) + +.. math:: + \mathcal{T} = \rho_{a} C_{\mathcal{T}} |\mathbf{v}_{a}|\mathbf{v}_{a} + +.. math:: + E = \rho_{a} C_{E} |\mathbf{v}_{a}|(q_{a} - q_{s}^{*}). + +In the above, :math:`\rho_{a}`, :math:`|\mathbf{v}_{a}`, :math:`\Theta_{a}`, and :math:`q_{a}` are the density, horizontal wind, potential temperature and specific humidity calculated at the lowest model level. :math:`c_{p}` is the heat capacity at constant pressure, :math:`\Theta_{s}` is the surface potential temperature and :math:`q_{s}^{*}` is the saturation specific humidity at the surface temperature. + +:math:`C_{S}`, :math:`C_{\mathcal{T}}` and :math:`C_{E}` are drag coefficients. These are calculated according to a simplified Monin-Obukhov similarity theory [Frierson2006a] in the module ``monin_obukhov_mod``, found in ``Isca/src/atmos_param/monin_obukhov/monin_obukhov.F90``. Roughness lengths for each flux can be specified as namelist parameters in ``idealised_moist_phys``. + +Land +```` +Land is implemented in Isca in 4 ways, via adjustment of (1) the roughness length, (2) the evaporative flux, (3) the albedo, (4) the mixed layer depth: + +(1) Any desired adjustments to roughness length over land are made in ``idealized_moist_phys_mod`` via the namelist parameter ``land_roughness_prefactor`` and are then fed to ``surface_flux_mod``. This parameter prescribes a prefactor to modify the roughness lengths used over land in the Monin-Obukhov calculations. The same modification prefactor is used for all fluxes. + +(2) Adjustments to the evaporative flux are made in ``surface_flux_mod``. 2 options are available: + + (a) Prefactors :math:`\alpha` and :math:`\beta` may be prescribed over land to modify the evaporative flux: + + .. math:: + E = \beta \rho_{a} C_{E} |\mathbf{v}_{a}|(q_{a} - \alpha q_{s}^{*}). + + :math:`\alpha` is set using ``land_humidity_prefactor`` and :math:`\beta` is set using ``land_evap_prefactor`` in the ``surface_flux_nml``. + + + (b) A simple bucket hydrology may be used following [Manabe1969]. In this case, hydrology is described by prescribing land with a bucket depth :math:`W` which can vary between 0, corresponding to an empty bucket, and a field capacity :math:`W_{FC}`, corresponding to a full bucket. The evaporation equation is then modified over land grid cells according to the bucket depth. + + If :math:`W \lt 0.75 W_{FC}`: + + .. math:: + E = \frac{W}{0.75 W_{FC}} \rho_{a} C_{E} |\mathbf{v}_{a}|(q_{a} - q_{s}^{*}) + + If :math:`W \ge 0.75 W_{FC}`: + + .. math:: + E = \rho_{a} C_{E} |\mathbf{v}_{a}|(q_{a} - q_{s}^{*}) + + Bucket namelist parameters are input via ``idealized_moist_phys_mod``. + +3.& 4. Adjustments to the albedo and mixed layer depth are made via namelist parameters in the module :mixed_layer_mod: found in ``Isca/src/atmos_spectral/driver/solo/mixed_layer.F90``. + + +Parametrization for gas giants +------------------------------ + +For modelling gas giants, the namelist option ``flux_heat_gp`` allows the user to prescribe an intrinsic heat flux at 1 bar, appropriate for a giant planet atmosphere where there is no surface [Schneider2009]. In practice, the subroutine ``gp_surface_flux`` will then impose an atmospheric heating :math:`Q_{gp}` at the lowest model level as: + +.. math:: + Q_{gp} = \frac{g \gamma}{c_{p} \Delta p} + +where :math:`g` is the gravitational acceleration, :math:`\gamma` is the value of ``flux_heat_gp``, :math:`c_{p}` is the heat capacity at constant pressure and :math:`\Delta p` is the thickness of the lowest model pressure level. To accelerate model spin-up, this intrinsic heat flux can be increased via the namelist option ``diabatic_acce``. Alternatively, Isca's column model configuration could be run to equilibrium. + +To turn on this parametrization, ``gp_surface`` should be set to True in the ``idealized_moist_phys`` namelist. + + +Namelist options +---------------- + +The namelist options for ``surface_flux_nml`` are listed below. These include options to limit evaporation over land, to use more simplified parametrisations, to account for salinity over ocean, and to help with issues resulting from truncation. We note that Isca is developed from the GFDL FMS model (https://github.com/NOAA-GFDL/FMS) and some options remain to enable old model configurations to be supported, as indicated in the table. + ++---------------------------+----------+-----------------------------------------------------------------------------------------+ +| Name | Default | Description | ++===========================+==========+=========================================================================================+ +|``no_neg_q`` | False | If q_atm_in (specific humidity input) is negative (because of numerical truncation), | +| | | then override with 0. | ++---------------------------+----------+-----------------------------------------------------------------------------------------+ +|``use_virtual_temp`` | True | If true, use virtual potential temp to calculate the stability of the surface layer. | +| | | If false, use potential temp. | ++---------------------------+----------+-----------------------------------------------------------------------------------------+ +|``alt_gustiness`` | False | If true, use an alternative formulation for gustiness calculation where a minimum bound | +| | | on the wind speed is used in flux calculations, with the bound equal to ``gust_const``. | ++---------------------------+----------+-----------------------------------------------------------------------------------------+ +|``gust_const`` | 1.0 | Constant for alternative gustiness calculation. | ++---------------------------+----------+-----------------------------------------------------------------------------------------+ +|``gust_min`` | 0. | Minimum gustiness used when alt_gustiness is set to False. | ++---------------------------+----------+-----------------------------------------------------------------------------------------+ +|``old_dtaudv`` | False | If true, the derivatives of surface wind stress w.r.t. the zonal wind and meridional | +| | | wind are each approximated by the same value. | ++---------------------------+----------+-----------------------------------------------------------------------------------------+ +|``use_mixing_ratio`` | False | GFDL LEGACY: If true, then use the saturation surface mixing ratio instead of specific | +| | | humidity in calculating surface moisture exchange. This provides the capability to run | +| | | the Manabe Climate form of the surface flux. | ++---------------------------+----------+-----------------------------------------------------------------------------------------+ +|``do_simple`` | False | If true, then approximate saturation surface specific humidity as: | +| | | :math:`q_{sat} = 0.622*e_{sat} / p_{surf}`. | +| | | If false: :math:`q_{sat} = 0.622*e_{sat} / (p_{surf} - e_{sat})` is used. | ++---------------------------+----------+-----------------------------------------------------------------------------------------+ +|``ncar_ocean_flux`` | False | Use NCAR climate model turbulent flux calculation described by [Large2004]. | ++---------------------------+----------+-----------------------------------------------------------------------------------------+ +|``ncar_ocean_flux_orig`` | False | GFDL LEGACY: Use NCAR climate model turbulent flux calculation described by [Large2004],| +| | | using the original GFDL implementation, which contains a bug in the specification of the| +| | | exchange coefficient for the sensible heat. Not recommended for new experiments. | ++---------------------------+----------+-----------------------------------------------------------------------------------------+ +|``raoult_sat_vap`` | False | If true, reduce saturation vapor pressures over ocean to account for seawater salinity. | ++---------------------------+----------+-----------------------------------------------------------------------------------------+ +|``land_humidity_prefactor``| 1.0 | Factor that multiplies the surface specific humidity over land. This is included to make| +| | | land 'dry'. If it is equal to 1, land behaves like ocean. If it is between 0 and 1, | +| | | this will decrease the evaporative heat flux in areas of land. Note that this can lead | +| | | to sign changes in the evaporative flux, and we find this becomes unstable over very | +| | | shallow mixed layer depths. | ++---------------------------+----------+-----------------------------------------------------------------------------------------+ +|``land_evap_prefactor`` | 1.0 | Factor that multiplies the evaporative flux over land. This is included to make land | +| | | 'dry'. If it is equal to 1, land behaves like ocean. If it is between 0 and 1, this will| +| | | decrease the evaporative heat flux in areas of land. This formulation avoids sign | +| | | changes in the evaporative flux and remains stable over very shallow mixed layer depths.| ++---------------------------+----------+-----------------------------------------------------------------------------------------+ +|``flux_heat_gp`` | 5.7 | Intrinsic heat flux imposed to describe the lower boundary of a giant planet atmosphere.| +| | | [Schneider2009]. Default is :math:`5.7 Wm^{-2}`, appropriate for Jupiter [Gierasch2000].| ++---------------------------+----------+-----------------------------------------------------------------------------------------+ +|``diabatic_acce`` | 1.0 | Multiplicative scaling factor for the temperature tendency in the giant planet scheme. | +| | | This can be used to speed up model spin-up. It will speed up the model spin-up if | +| | | greater than 1. A similar factor can also be applied for giant planet radiation. Note | +| | | that an alternative way to accelerate spin-up is to run Isca's column model to | +| | | equilibrium. | ++---------------------------+----------+-----------------------------------------------------------------------------------------+ + + + +Diagnostics +----------- + +No diagnostics are saved within the ``surface_flux`` module itself. Diagnostics related to surface exchange are available via the `atmosphere` diagnostic list in ``idealized_moist_phys.F90``, and the `mixed_layer` diagnostic list in ``mixed_layer.F90``. + + +Relevant modules and subroutines +-------------------------------- + +Modules relevant to this one include: + +:idealized_moist_phys: Momentum flux diagnostics are output here. A land roughness prefactor can also be applied in this module, which is used to alter the roughness lengths that are fed to ``surface_flux_mod``. +:mixed_layer_mod: Diagnostics for fluxes of sensible and latent heat are output here. + + +References +---------- + +| [Frierson2006a]_ +| [Gierasch2000]_ +| [Large2004]_ +| [Manabe1969]_ +| [Schneider2009]_ +| [VallisEtAl2018]_ + + +Authors +---------- +This documentation was written by Ruth Geen, peer reviewed by Marianne Pietschnig, and quality controlled by Ross Castle. + diff --git a/docs/source/modules/topography.rst b/docs/source/modules/topography.rst new file mode 100644 index 000000000..cd3db1139 --- /dev/null +++ b/docs/source/modules/topography.rst @@ -0,0 +1,129 @@ +Topography and Land Masks in Isca +================== + +This guide covers: + +1. How to implement a land mask and/or topography (using the T42 land mask provided). +2. How to create/modify topography to suit your own needs (using the python tools included in Isca). +3. The ``topography.F90`` module, which used to control land masks/topography but has now been superceeded. It still retains some useful functions however. + +1 - Implementing Topography in Isca +----------------------------------- + +This is relatively simple to do, assuming that the topography/land mask you are using has already been interpolated to the same spectral grid you are running the model at. If running at T42, you can use the one provided (``input/land_masks/era_land_t42.nc``). If not, you can interpolate fairly easily from any high resolution data, or for configurations of Earth continents, please refer to section 2 of this guide. + +- The land mask is a binary field the size of the spectral model grid (e.g. 64 x 128 for T42) that contains a ``1`` if that grid box is designated as being land or a ``0`` if it is not. +- Topography is a separate field, again the size of the spectral model grid, containing the surface height for that grid box in meters. +- Often a 'land mask' will contain both these fields. + +**Can I have land without topography?** Yes - For idealized modelling sometimes it is useful to have the land/sea contrasts (different albedo, no ocean exchange and different surface parameters) without the complexity of topography. It is also possible to have topography without land (aquamountains). + +Implementing these options require different namelist values. There are two namelists that need to be told about land and topograhy; ``idealized_moist_phys_nml`` (land mask) and ``spectral_init_cond_nml`` (topography). + +idealized_moist_phys +^^^^^^^^^^^^^^^^^^^^ +NOTE: see the :ref:`idealized moist phys ` documentation for additional guidance on this namelist. + +The ``idealized_moist_phys`` module is primarily concerned with the land mask. Its job is to pass the information about the land mask to other modules, which in the first instance are the ``mixed_layer`` and the ``surface_flux`` modules, which deal with ocean and surface exchanges with the atmosphere respectively. Both modules need to know where there is ocean/land so correct physics can be computed in the correct location. In addition to passing this information, ``idealized_moist_phys`` will also use the land mask for some other model features, for example some land based parameters and calculating buckets if using bucket hydrology. + +The ``idealized_moist_phys`` namelist options to include the appropriate land mask are: + +| ``land_option : 'input'`` (the default is ``'none'``, - this is essentially the on switch) +| ``land_file_name : 'INPUT/era_land_t42.nc'`` (using the example of the provided land file) +| ``land_field_name : 'land_mask'`` (this is the default and doesn't need to be specified if that is correct) + +spectral_init_cond +^^^^^^^^^^^^^^^^^^ +Now there is a land mask, topography can be included if desired. If not, this step can be ignored. + +The namelist options below are to include topography from the same file as the land mask. It can be a different file, as long as the grid size is still correct and the height units is meters. There are multiple other options for including topography, some of which are unused at the moment. + +| ``topography_option : 'input'`` (Tell model to get topography from input file) +| ``topog_file_name : 'era_land_t42.nc'`` (Again, we use the provided file as the example) +| ``topog_field_name : 'zsurf'`` (The height field name) +| ``land_field_name : 'land_mask'`` (The land field name) + +NOTE: ``zsurf`` and ``land_mask`` are the default values in Isca and these are the names used in the provided land mask so does not need to be included when using it. + +This set up is the standard way to use topography in Isca, using the ``spectral_init_cond`` module. The module deals with topography, setting up the shape of the model boundary layer. + +Other ``topography_option`` options are available, but not widely used by the Isca team: + +- No topography - the default. +- Flat topography - the surface geopotential is 0. +- Interpolated topography - where it will call the ``topography`` module below, however this set up is not currently used. +- Gaussian Topography - Simple Gaussian-shaped mountains are generated from specified parameters. + +2 - Creating Custom Topography +------------------------------ + +We provide python code that allows land files to be generated for a range of scenarios. This code is found in ``src/extra/python/isca/land_generator_fn.py``. Some tweaking of the code will be needed to suit the users requirements. + +Land Options +^^^^^^^^^^^^ +- 'square' (default) Square block of land with boundaries specified by boundaries keyword, a list of 4 floats in the form [S,N,W,E] +- 'continents_old' Choose continents from the original continent set-up adapted from the Sauliere 2012 paper (Jan 16), including North and South America, Eurasia, and Africa. +- 'continents' Choose continents from a newer continet set-up allowing addition of India, Australia, and South East Asia. +- If continents keyword is set to 'all' (default), then this will include all possibilities for the given set-up. Alternatively, continents can be set to a list of strings allowing any combination of continents. Instructions for this are available in the code. + +Topography Options: +^^^^^^^^^^^^^^^^^^^ +- 'none' (default) Topography set to zero everywhere +- 'sauliere2012' Choose mountains from [Sauliere2012]_ configuration using mountains keyword. Default is 'all', alternatively only 'rockys' or 'tibet' may be specified +- 'gaussian' Use parameters specified in topo_gauss keyword to set up a Gaussian mountain. topo_gauss should be a list in the form: [central_lat, central_lon, radius_degrees, std_dev, height] + +3 - Topography Module (topography.F90) +-------------------------------------- + +Summary +^^^^^^^ +The ``topography`` module contains numerous routines for creating land surface topography fields and land-water masks for specified latitude-longitude grids. It does this by interpolating from a high resolution netCDF file, which is designated in the namelist. The module was originally written to work with the 1/6 degree Navy mean topography and water data sets. However, any netCDF file can be used as an input in the namelist, providing that the file contains grid box boundaries, (which should be named in the namelist) and whether degrees or radians is specified in the namelist. + +As mentioned above, this module is generally not called anymore, in the normal Isca set up, land masks are actually specified through the ``idealized_moist_phys`` module, and the model topography through the ``spectral_init_cond`` module. The main use of it is to provide the "subgrid topography" when using the orographic gravity wave drag scheme (``mg_drag``). + +The fields that can be generated with this module are mean and standard deviation of topography within the specified grid boxes; and land-ocean (or water) mask and fractional area. The interpolation conserves the area weighted average of the input data by using the ``horiz_interp`` module. + +Namelist options +^^^^^^^^^^^^^^^^ +| ``topog_file`` - The topography file that you wish to use. +| ``water_file`` - The water data file (not commonly used, not provided in the Isca release) + +For a typical Earth set up the namelist would simply be: + +``'topog_file': navy_topography.nc`` + +This essentially just points the ``topography`` module to this file when it is asked for by another subroutine, e.g. ``mg_drag``. + +Diagnostics +^^^^^^^^^^^ +There are no diagnostics available directly through this module. The subgrid topography variance can be obtained through damping driver by asking for ``sgsmtn`` (``mg_drag`` must be turned on). + +Relevant subroutines +^^^^^^^^^^^^^^^^^^^^ + +NOTE: Some subroutines have dimensional variants, e.g. interp_topog has both a 1d and 2d variant. + +**Public Subroutines** + +These subroutines are used by the topography module to produce the land-masks etc that are being asked for by the user. They are largely self explanatory. + +| ``get_topog_mean`` returns the mean height from a region of the topography file so that that value can be used as the value when interpolating onto a smaller grid. +| ``get_topog_stdev`` returns the standard deviation from a region of the topography file so that that value can be assoiated with the same region when interpolating onto a smaller grid. +| ``get_ocean_frac`` returns the fraction of the land mask that is covered by ocean. +| ``get_ocean_mask`` returns an ocean/land mask +| ``get_water_frac`` returns the fraction of the land mask that is covered by water. +| ``get_water_mask`` returns a water/land mask +| ``gaussian_topog_init`` and ``get_gaussian_topog`` call the gaussian topography module. + +**Private Subroutines** + +There are other subroutines called by the above. These are listed below: +``open_topog_file``, ``interp_topog``, ``find_indices``, ``input_data``, ``interp_water``, ``determine_ocean_points``, ``read_namelist``. + +References +---------- +See [Sauliere2012]_ for the topography option in ``land_generator_fn.py``. + +Authors +------- +This documentation was written by Ross Castle, peer reviewed by Ruth Geen, and quality controlled by Marianne Pietschnig. diff --git a/docs/source/modules/two_stream_gray_rad.rst b/docs/source/modules/two_stream_gray_rad.rst new file mode 100644 index 000000000..61cf9722d --- /dev/null +++ b/docs/source/modules/two_stream_gray_rad.rst @@ -0,0 +1,431 @@ + +Radiative transfer: Simple gray and multi-band radiation schemes. +======================================================================================= + +Documentation for ``two_stream_gray_rad_mod``. + + +Summary +------- +This module includes different simple configurations for solving the two stream radiative transfer equations. There are three semi-gray configurations available (gray in shortwave and longwave), and one intermediate multi-band configuration (gray in shortwave, multi-band in longwave). These configurations are selected by setting the ``rad_scheme`` namelist option. + ++------------------+-------------------------------+----------------------------------------------------+----------------------+ +| ``rad_scheme`` | Short description | Reference | ++------------------+-------------------------------+----------------------------------------------------+----------------------+ +| ``'FRIERSON'`` | | Semi-gray scheme with prescribed longwave and shortwave | [Frierson2006a]_ | +| | | optical depths. | | ++------------------+-------------------------------+----------------------------------------------------+----------------------+ +| ``'BYRNE'`` | | Semi-gray scheme with longwave optical depth dependent on | | [Byrne2013]_ | +| | | water vapour content and :math:`\text{CO}_{2}` concentration. Shortwave | | [VallisEtAl2018]_ | +| | | optical depth is prescribed. | | ++------------------+-------------------------------+----------------------------------------------------+----------------------+ +| ``'GEEN'`` | | Multi-band scheme with two longwave bands and one shortwave | | [Geen2016]_ | +| | | band. One longwave band corresponds to an infrared window | | [VallisEtAl2018]_ | +| | | region (:math:`8-14\,\mu\,\text{m}`) and the second corresponds to all other | | +| | | infrared wavelengths (:math:`>4\,\mu\,\text{m}`). Longwave and shortwave optical | | +| | | depths depend on water vapour content and :math:`\text{CO}_{2}` concentration. | | +| | | | | +| | | Note: the Geen scheme is currently only stable when run in a fixed SST | | +| | | configuration. A version stable with a slab ocean is expected by 30/11/2020. | | ++------------------+-------------------------------+----------------------------------------------------+----------------------+ +| ``'SCHNEIDER'`` | | Semi-gray scheme for use in **giant planet** simulations. Longwave | [Schneider2009]_ | +| | | and shortwave optical depths are prescribed. Does not require a | | +| | | surface temperature as input, and allows specification of an | | +| | | interior heat flux. | | ++------------------+-------------------------------+----------------------------------------------------+----------------------+ + + + + +For each of these schemes diurnally-and-seasonally averaged, and time-dependent options for the incoming solar radiation are available. + +In the next few sections we will describe the main options available to the user for solving the radiative transfer equations, and prescribing the insolation. Namelist options and their defaults, and available output diagnostics are summarised at the end of the documentation. + +Frierson/Byrne schemes +---------------------- + +Each of these schemes solve the same equations to compute the longwave and shortwave fluxes. The difference between them lies in the definition of the longwave optical depths. + +| **Longwave** +| Longwave radiative transfer is described by the following two equations: + +.. math:: + \frac{\text{d}I_{+}}{\text{d}\tau}=I_{+}-\sigma T^{4} \\ + \frac{\text{d}I_{-}}{\text{d}\tau}=\sigma T^{4} - I_{-} + +:math:`I_{+}` and :math:`I_{-}` are the upward and downward longwave fluxes, and :math:`\tau` is the longwave optical depth. At the surface, :math:`I_{+}(p=p_{s}) = \sigma T_{s}^{4}`, and at the top of atmosphere :math:`I_{-}(p=0) = 0`. + +| **Shortwave** +| The downward shortwave flux :math:`I_{-}^{\ast}` is given by + +.. math:: + I_{-}^{\ast} = S\exp(-\tau^{\ast}) + +where :math:`\tau^{\ast}` is the shortwave optical depth, and :math:`S` is the incoming solar radiation. The implementation of the shortwave band in the Frierson and Byrne schemes assumes that there is no scattering within the atmosphere, and shortwave radiation is only absorbed from the downward stream. All reflection occurs at the surface. The upward flux of shortwave :math:`I_{+}^{\ast}` is not absorbed and escapes through the top of atmosphere, i.e. :math:`I_{+}^{\ast}=\alpha I_{-}^{\ast}(p=p_s)`, where :math:`\alpha` is the surface albedo. Note that the surface albedo is set by ``mixed_layer_mod``. + +| **Longwave optical depth** +| With ``rad_scheme`` set to ``'FRIERSON'``, the longwave optical depth is specified as a function of pressure + +.. math:: + \tau = \tau_{0}\left[f_{l}\left(\frac{p}{p_0}\right)+(1-f_{l})\left(\frac{p}{p_0}\right)^{k}\right] + +where :math:`p_0=10^{5}\,\text{Pa}` is a reference pressure, :math:`f_{l}` sets how large the linear term is relative to the :math:`p^{k}` term. :math:`k` is a constant exponent (default 4) which determines the pressure dependence of optical depth. The linear term is included to reduce relaxation times in the stratosphere. The surface longwave optical depth :math:`\tau_{0}` is given by + +.. math:: + \tau_{0} = \kappa[\tau_{0e} + (\tau_{0p}-\tau_{0e})\sin^{2}\theta] + +:math:`\tau_{0e}` is the value of the surface optical depth at the equation, and :math:`\tau_{0p}` is the value at the pole. :math:`\kappa` is a parameter that can be used to scale the optical depth (default 1). + +When ``rad_scheme`` is set to ``'BYRNE'``, the longwave optical depth is calculated as a function of specific humidity :math:`q`, :math:`\text{CO}_{2}` mixing ratio, and pressure + +.. math:: + \frac{\text{d}\tau}{\text{d}(p/p_{0})} = a\mu+bq+0.17\log(\text{CO}_{2}/360) + +In the equation above, :math:`a` and :math:`b` are constant absorption coefficients, and :math:`\mu` is a scaling parameter intended to represent absorption by well-mixed gases (default value 1). + +| **Shortwave optical depth** +| Both the Frierson and Byrne schemes define the shortwave optical depth as + +.. math:: + \tau^{\ast}= \tau^{\ast}_0\left(\frac{p}{p_0}\right)^{k^{\ast}} + +where :math:`k^{\ast}` is a constant exponent that sets the pressure dependence of :math:`\tau^{\ast}`. :math:`\tau_{0}^{\ast}` is the shortwave optical depth at the surface, given by + +.. math:: + \tau^{\ast}_{0}=(1-\Delta\tau^{\ast}\sin^{2}\theta)*\tau_{0e}^{\ast} + +where :math:`\tau_{0e}^{\ast}` is the surface optical depth at the equator, and :math:`\Delta\tau^{\ast}` determines the variation of shortwave optical depth with latitude. + + + +Schneider giant planet scheme +----------------------------- + +This scheme is suitable to use for giant planet experiments where there is no solid surface at the bottom of the model atmosphere. It is selected by setting ``rad_scheme`` to ``'SCHNEIDER'``. + + +| **Longwave** +| The Schneider scheme solves the same equations for longwave radiative transfer as the Frierson/Byrne schemes. The only difference is in the lower boundary condition, where energy balance is enforced and the upward thermal radiative flux is set equal to the sum of the downward solar and thermal radiative fluxes, :math:`I_{+}(p=p_{s}) = I_{-}(p=p_{s}) + I_{-}^{\ast}(p=p_{\text{s}})`. An intrinsic heat flux from the planet's deep interior can also be specified, this is done in ``surface_flux_mod``. + +| **Shortwave** +| The downward shortwave flux :math:`I_{-}^{\ast}` is given by + +.. math:: + I_{-}^{\ast} = S(1-\alpha_{\text{b}})\exp(-\Gamma\tau^{\ast}) + +where + +.. math:: + \Gamma = 2\sqrt{1-\tilde{\omega}}\sqrt{1-\tilde{\omega}\gamma} + +and + +.. math:: + \alpha_{\text{b}}=\frac{\sqrt{1-\tilde{\omega}\gamma}-\sqrt{1-\tilde{\omega}}}{\sqrt{1-\tilde{\omega}\gamma}+\sqrt{1-\tilde{\omega}}} + +is the Bond albedo. Here, :math:`\tilde{\omega}=0.8` is the single-scattering albedo, and :math:`\gamma=1-2f_{b}` is the asymmetry factor, where :math:`f_{b}=0.398` is the fraction of radiation back scattered. + +The only reflection of shortwave radiation occurs at the top of atmosphere where a fraction :math:`(1-\alpha_{\text{b}})` is removed from the incoming solar radiation. + +There is no modelled upward flux of shortwave radiation in the atmosphere :math:`I_{+}^{\ast}=0`. Instead any shortwave radiation that reaches the bottom of the atmosphere is re-added to the upward longwave beam as a flux through the lower boundary. + +| **Longwave optical depth** +| For the Schneider scheme, longwave optical depth is a simple function of pressure + +.. math:: + \tau = \tau_{0,\text{gp}}\left(\frac{p}{p_{0}}\right)^{k_{\text{gp}}} + +where :math:`\tau_{0,\text{gp}}=80.0` is the longwave optical depth at pressure :math:`p_{0}` and :math:`k_{\text{gp}}=2.0` is a constant exponent that sets the pressure dependence of :math:`\tau`. + +| **Shortwave optical depth** +| The shortwave optical depth takes a similar functional form to the longwave optical depth + +.. math:: + \tau^{\ast} = \tau^{\ast}_{0,\text{gp}}\left(\frac{p}{p_{0}}\right)^{k^{\ast}_{\text{gp}}} + +where :math:`\tau^{\ast}_{0,\text{gp}}=3.0` is the shortwave optical depth at pressure :math:`p_{0}` and :math:`k^{\ast}_{\text{gp}}=1.0` is a constant exponent that sets the pressure dependence of :math:`\tau^{\ast}`. + +| **Note on input parameters for this scheme** +| At present, each of the input parameters :math:`\tilde{\omega}`, :math:`f_{b}`, :math:`\tau_{0,\text{gp}}`, :math:`k_{\text{gp}}`, :math:`\tau^{\ast}_{0,\text{gp}}`, and :math:`k^{\ast}_{\text{gp}}` are hardcoded and are not available as namelist options. There default values are given in the description of the scheme above. The code could be easily modified if a user wished to vary these parameters from the namelist. + + +Geen scheme +----------- + +The Geen scheme provides an intermediate option between gray radiation and more complete descriptions of radiative transfer (e.g., the correlated-:math:`k` schemes SOCRATES and RRTM). It has two infrared bands and one solar band. The shortwave band (:math:`<4\,\mu\,\text{m}`) treats all solar radiation. Two long-wave bands treat absorption: one in the infrared region of the spectral (:math:`8-14\,\mu\,\text{m}`), and the other in all other longwave wavelengths (:math:`<4\,\mu\,\text{m}`, non-window). + +Note: the Geen scheme is currently only stable when run in a fixed SST configuration. A version stable with a slab ocean is expected by 30/11/2020. + +| **Longwave** +| Longwave radiative transfer is described by the following set of equations. In the non-window region: + +.. math:: + \frac{\text{d}I_{+}^{\text{nw}}}{\text{d}\tau^{\text{nw}}}=I_{+}^{\text{nw}}-R^{\text{nw}}\sigma T^{4} \\ + \frac{\text{d}I_{-}^{\text{nw}}}{\text{d}\tau^{\text{nw}}}=R^{\text{nw}}\sigma T^{4} - I_{-}^{\text{nw}} + +and in the window region: + +.. math:: + \frac{\text{d}I_{+}^{\text{win}}}{\text{d}\tau^{\text{win}}}=I_{+}^{\text{win}}-R^{\text{win}}\sigma T^{4} \\ + \frac{\text{d}I_{-}^{\text{win}}}{\text{d}\tau^{\text{win}}}=R^{\text{win}}\sigma T^{4} - I_{-}^{\text{win}} + +The superscripts :math:`^{\text{nw}}` and :math:`^{\text{win}}` refer to the non-window and window regions, respectively. :math:`R^{\text{win}}` and :math:`R^{\text{nw}}=1-R^{\text{win}}` are the fraction irradiances in the non-window and window regions. + + +| **Shortwave** +| The Geen scheme solves the same equations for shortwave radiative transfer as the Frierson/Byrne schemes. The only difference is in the specification of the shortwave optical depth (see below). + +As with the Frierson and Byrne schemes, the implementation of the shortwave band assumes that there is no scattering within the atmosphere, and shortwave radiation is only absorbed from the downward stream. All reflection occurs at the surface. The upward flux of shortwave :math:`I_{+}^{\ast}` is not absorbed and escapes through the top of atmosphere. + +| **Longwave optical depth** +| The longwave optical depth in the Geen scheme is defined seperately for the non-window and window regions. In the window region + +.. math:: + \frac{\text{d}\tau^{\text{win}}}{\text{d}(p/p_{0})} = a_{\text{win}}+b_{\text{win}}q+c_{\text{win}}q^{2}+0.0954\log(\text{CO}_{2}/360) + +and in the non-window region + +.. math:: + \frac{\text{d}\tau^{\text{nw}}}{\text{d}(p/p_{0})} = a_{\text{nw}}+b_{\text{nw}}\log(c_{\text{nw}}q+1)+0.2023\log(\text{CO}_{2}/360) + +where the :math:`a`, :math:`b`, and :math:`c`'s are constant absorption coefficients that may be specified in the namelist. The default values for these coefficients were fitted to output from `Santa Barbara DISORT Atmospheric Radiative Tranfer 60` (SBDART) [Ricchiazzi1998]_. + +| **Shortwave optical depth** +| In the shortwave, the optical depth in the Geen scheme is specified as + +.. math:: + \frac{\text{d}\tau^{\ast}}{\text{d}(p/p_{0})} = a_{\text{sw}}+b_{\text{sw}}(\tau^{\ast})q+c_{\text{sw}}\log(\text{CO}_{2}/360) + +where + +.. math:: + \log[b_{\text{sw}}(\tau^{\ast})]=\frac{0.01887}{\tau^{\ast}+0.009522}+\frac{1.603}{(\tau^{\ast}+0.5194)^{2}}. + +:math:`a_{\text{sw}}=0.0596` and :math:`c_{\text{sw}}=0.0029`. As it stands, :math:`a_{\text{sw}}`, :math:`b_{\text{sw}}`, and :math:`c_{\text{sw}}` are hardcoded and not modifiable via the namelist. As with the longwave absorption coefficients, the default values for the absorption coefficients were fitted to SBDART output. + +Incoming solar radiation +------------------------ + +For each of the radiative transfer schemes, diurnally-and-seasonally averaged, and time-dependent options for the incoming solar radiation are avaialable. The user selects whether diurnally-and-seasonally averaged or time-dependent solar forcing is used by setting the namelist option ``do_seasonal``. + +| **Diurnally-and-seasonally averaged insolation** +| Diurnally-and-seasonally averaged insolation is selected by setting ``do_seasonal = False``. + +For the Frierson, Byrne, and Geen schemes, setting ``do_seasonal = False`` imposes a :math:`P_{2}` (second legendre polynomial) insolation profile, which is designed to approximate the Earth's seasonally averaged insolation distribution. When this option is selected, there is no diurnal cycle (i.e. the forcing is fully time-independent). The incoming solar radiation then takes the following form: + +.. math:: + S &= \frac{S_{0}}{4}[1+\Delta_{S}P_{2}(\theta)+\Delta_{\text{sw}}\sin\theta] \\ + P_{2} &= (1 - 3\sin^{2}\theta)/4 + +where :math:`S_{0}` is the solar constant. :math:`\Delta_{S}` is used to set the amplitude of the :math:`P_{2}` insolation profile between the equator and pole, and :math:`\Delta_{\text{sw}}` (default 0) can be used to further modify this with a :math:`\sin\theta` profile. When :math:`\Delta_{\text{sw}}=0`, the insolation difference between the equator and pole is :math:`\Delta S= 3\Delta_{S}/4\times S_{0}/4`. + +For the Schneider giant planet scheme, setting ``do_seasonal = False`` imposes an insolation profile that varies with :math:`\cos\theta`, + +.. math:: + S = \frac{S_{0}}{\pi}\cos\theta + +which corresponds to the latitudinal distribution of radiation received by a planet with no obliquity (perpetual equinox). As with the simple :math:`P_{2}` insolation for the Frierson, Byrne, and Geen schemes, there is no diurnal cycle. + +| **Time-dependent insolation** +| All of the radiation schemes contained in this module can be run with time-dependent insolation, selected by setting ``do_seasonal = True``. In this case, ``astronomy_mod`` is used to calculate the zenith angle and planet-star distance at each location and time. This information is then used by the ``two_stream_gray_rad`` module to calculate the top of atmosphere insolation as a function of time. + +``astronomy_mod`` calculates the zenith angle and planet-star distance as a function of the following orbital and planetary parameters: obliquity, eccentricity, semi-major axis, longitude of perihelion (w.r.t NH autumn equinox), orbital period, and rotation rate. These options must be specified to ``astronomy_mod_nml``. An input parameter in ``two_stream_gray_rad``, ``equinox_day`` determines the time of year when NH autumn equinox occurs. + +``two_stream_gray_rad`` then calculates the insolation as + +.. math:: + S = S_{0}\cos\zeta\left(\frac{a}{r}\right)^{2} + +where :math:`\zeta` is the zenith angle, :math:`a` is the semi-major axis of the orbital ellipse, and :math:`r` is the time-varying planet-star distance. NB: :math:`(a/r)^{2}` is called ``rrsun`` in the code. + +If the user wishes, they may average incoming solar radiation over a period :math:`\Delta t_{\text{avg}}` (units :math:`\text{s}`). This allows the user to have seasonally varying forcing without a diurnal cycle, for example. To achieve diurnally averaged insolation for a planet with the Earth's length of day one would set :math:`\Delta t_{\text{avg}}=86400.0\,\text{s}`. + +There is also an option to run perpetually on one day by setting the namelist variable ``solday``. For example if Northern Hemisphere autumn equinox was set to occur on day :math:`270` of a :math:`360` day year, then one could run a perpetual solstice simulation by setting ``solday=180``. This can be used in conjunction with an appropriate choice for :math:`\text{d}t_{\text{avg}}` to remove the diurnal cycle in such an experiment. + + + +Namelist options +---------------- + + +The namelist options for **two_stream_gray_rad_nml** are listed below. + + +**Namelist option to choose scheme** + +:rad_scheme: String choosing the radiation scheme. Options are ``'FRIERSON'``, ``'BYRNE'``, ``'GEEN'``, ``'SCHNEIDER'``. Default option is ``'FRIERSON'``. + +**Namelist options for Frierson scheme longwave optical depth** + +:ir_tau_eq: Surface longwave optical depth at equator. Default :math:`6.0`. +:ir_tau_pole: Surface longwave optical depth at pole. Default :math:`1.5`. +:odp: Frierson optical depth scaling parameter :math:`\kappa`. Default :math:`1.0`. +:linear_tau: :math:`f_l`. Determines partitioning between linear term and :math:`p^{k}` term in Frierson longwave optical depth. Default :math:`0.1`. +:wv_exponent: Pressure exponent, :math:`k` in definition of optical depth. Default :math:`4.0`. + +**Namelist options for Byrne scheme longwave optical depth** + +:bog_a: Absorption coefficient :math:`a` in Byrne longwave optical depth. Default :math:`0.8678`. +:bog_b: Absorption coefficient :math:`b` in Byrne longwave optical depth. Default 1997.9. +:bog_mu: Scaling parameter :math:`\mu` in Byrne longwave optical depth. Default 1.0. + +**Namelist options for Frierson/Byrne scheme shortwave optical depth** + +:atm_abs: Shortwave optical depth at the equator :math:`\tau_{0e}^{\ast}`. Default :math:`0.0`. +:sw_diff: Amplitude of latitudinal optical depth variation :math:`\Delta\tau^{\ast}`. Default :math:`0.0`. +:solar_exponent: Pressure exponent :math:`k^{\ast}`. Default value is :math:`4.0`. + +| **Namelist options for Geen multi-band scheme** +| Note: for the Geen scheme, the input parameters determining the shortwave optical depth are hardcoded and cannot be set from the namelist (see scheme description above for their default values). Thus, only parameters determining the longwave optical depth are listed here. + + +:window: Window fraction :math:`R^{\text{win}}` for longwave radiative transfer. Default value :math:`0.3732`. +:ir_tau_co2_win: Absorption coefficient :math:`a_{\text{win}}` in longwave optical depth. Default value :math:`0.2150`. +:ir_tau_wv_win1: Absorption coefficient :math:`b_{\text{win}}` in longwave optical depth. Default value :math:`147.11`. +:ir_tau_wv_win2: Absorption coefficient :math:`c_{\text{win}}` in longwave optical depth. Default value :math:`1.0814\times10^{4}`. +:ir_tau_co2: Absorption coefficient :math:`a_{\text{nw}}` in longwave optical depth. Default value :math:`0.1`. +:ir_tau_wv1: Absorption coefficient :math:`b_{\text{nw}}` in longwave optical depth. Default value :math:`23.8`. +:ir_tau_wv2: Absorption coefficient :math:`c_{\text{nw}}` in longwave optical depth. Default value :math:`254.0`. + + +| **Namelist options for Schneider giant planet scheme** +| Note: for the Schneider scheme, the input parameters determining shortwave and longwave optical depth are currently hardcoded and cannot be set from the namelist (see scheme description above for their default values). Thus, these parameters are not listed here. + +:diabatic_acce: Multiplicative scaling factor for temperature tendency due to radiation in Schneider scheme. This can be used to speed up model spin-up. It will speed up the model spin-up if greater than 1. Default value is 1. + +**Namelist options for incoming solar radiation** + +:do_seasonal: Sets whether insolation is diurnally-and-seasonally averaged (``FALSE``), or time dependent (``TRUE``). Default ``FALSE``. +:solar_constant: The solar constant :math:`S_{0}`. Default value: :math:`1360.0\,\text{W}\,\text{m}^{-2}`. + +The following namelist options are used when ``rad_scheme`` is set to ``'FRIERSON'``, ``'BYRNE'`` or ``'GEEN'`` and ``do_seasonal=FALSE``: + +:del_sol: Parameter :math:`\Delta_{S}` determining :math:`P_{2}` insolation amplitude. Default value: :math:`1.4`. +:del_sw: Parameter :math:`\Delta_{\text{sw}}` defining magnitude of :math:`\sin\theta` modification to :math:`P_{2}` insolation profile. Default value: :math:`0.0`. + +The following namelist options are used when ``do_seasonal=TRUE``: + +:use_time_average_coszen: ``TRUE`` or ``FALSE``. If ``TRUE``, average :math:`\cos\zeta` (:math:`\zeta` is zenith angle) over the period ``dt_rad_avg``. For example, for the Earth's diurnal period, ``use_time_average_coszen=TRUE`` and ``dt_rad_avg=86400.`` would achieve diurnally averaged insolation. +:dt_rad_avg: Averaging period (units: seconds) for time-dependent insolation :math:`\Delta t_{\text{avg}}`. Default=-1 sets averaging period to model timestep. +:solday: Day of year to run time-dependent insolation perpetually. If negative, the option to run perpetually on a specific day is not used. Default -10. +:equinox_day: Fraction of year [0,1] where Northern Hemisphere autumn equinox occurs. Default = 0.75 (e.g. end of September for 360 day year). + +**Namelist options for setting carbon dioxide concentration** + +:do_read_co2: ``TRUE`` or ``FALSE``. If ``TRUE``, reads time-varying :math:`\text{CO}_{2}` concentration from an input file [needs to be 4D (3 spatial dimensions and time), but no spatial variation should be defined (the code only reads in maximum value at a given time)]. Default ``FALSE``. +:carbon_conc: Prescribed :math:`\text{CO}_{2}` (units: ppmv). Used if ``do_read_co2=FALSE``. Default value :math:`360.0\,\text{ppmv}`. +:co2_file: Name of :math:`\text{CO}_{2}` file to read (note, should be specified without .nc appendix). Default ``'co2'``. +:co2_variable_name: Name of :math:`\text{CO}_{2}` variable in :math:`\text{CO}_{2}` file. Default ``'co2'``. + +**Important parameters not set in two_stream_gray_rad_nml** + +:pstd_mks: This is used as the reference pressure :math:`P_{0}` in the Frierson/Byrne/Schneider shortwave optical depth, and the Frierson/Schneider longwave optical depth. It is set in ``constants_mod_nml``. Default value is :math:`10^{5}\,\text{Pa}`. Note: this should be changed to :math:`3\times10^{5}\,\text{Pa}` for the giant planet configuration. + + + +:albedo_value: The surface albedo :math:`\alpha` used by the Frierson/Byrne/Geen schemes at the lower boundary is set in ``mixed_layer_nml``. Default value is :math:`0.06` for a simple homogeneous slab ocean surface. The albedo can vary spatially if land or ice is introduced. For more details, see the documentation for ``mixed_layer_mod``. + +:flux_heat_gp: A prescribed heat flux through the lower boundary can be added for the giant planet case. This is set in ``surface_flux_nml``. Default value is :math:`5.7\,\text{W}\,\text{m}^{-2}`. + +The following astronomical parameters are set in ``astronomy_mod_nml``. They are used if ``do_seasonal=True``. + +:ecc: Orbital eccentricity. Default :math:`0.0`. +:obliq: Obliquity. Default :math:`23.439` degrees. +:per: Longitude of perihelion (point in orbit when planet is closest to star) with respect to autumnal equinox in Northern Hemisphere. Default :math:`102.932` degrees. + +The following astronomical parameters are set in ``constant_nml``. They will be used to calculate the diurnal period if ``do_seasonal=True``. + +:orbital_period: Orbital period in seconds. Default is :math:`365.25\times86400.0\,\text{s}`. Only used if ``calendar`` is set to ``'no_calendar'`` in ``main_nml``. +:omega: Planetary rotation rate in :math:`s^{-1}`. Default value is :math:`7.29\times10^{-5}\,\text{s}^{-1}` + + + +Diagnostics +----------- + +These are the diagnostics associated with the ``two_stream_gray_rad`` module. + + ++-------------------+-------------------------------------+------------------------------------+ +| Name | Description | Units | ++===================+=====================================+====================================+ +| olr | Outgoing longwave radiation. | :math:`\text{W}\,\text{m}^{-2}` | ++-------------------+-------------------------------------+------------------------------------+ +| swdn_sfc | Absorbed shortwave at surface. | :math:`\text{W}\,\text{m}^{-2}` | ++-------------------+-------------------------------------+------------------------------------+ +| swdn_toa | Shortwave flux down at top of | :math:`\text{W}\,\text{m}^{-2}` | +| | atmosphere. | | ++-------------------+-------------------------------------+------------------------------------+ +| net_lw_surf | Net upward longwave flux at | :math:`\text{W}\,\text{m}^{-2}` | +| | surface. | | ++-------------------+-------------------------------------+------------------------------------+ +| lwdn_sfc | Longwave flux down at surface. | :math:`\text{W}\,\text{m}^{-2}` | ++-------------------+-------------------------------------+------------------------------------+ +| lwup_sfc | Longwave flux up at surface. | :math:`\text{W}\,\text{m}^{-2}` | ++-------------------+-------------------------------------+------------------------------------+ +| tdt_rad | Temperature tendency due to | :math:`\text{K}\,\text{s}^{-1}` | +| | radiation. | | ++-------------------+-------------------------------------+------------------------------------+ +| tdt_solar | Temperature tendency due to | :math:`\text{K}\,\text{s}^{-1}` | +| | solar radiation. | | ++-------------------+-------------------------------------+------------------------------------+ +| flux_rad | Total radiative flux (positive | :math:`\text{W}\,\text{m}^{-2}` | +| | up). | | ++-------------------+-------------------------------------+------------------------------------+ +| flux_lw | Net longwave radiative flux | :math:`\text{W}\,\text{m}^{-2}` | +| | (positive up). | | ++-------------------+-------------------------------------+------------------------------------+ +| flux_sw | Net shortwave radiative flux | :math:`\text{W}\,\text{m}^{-2}` | +| | (positive up). | | ++-------------------+-------------------------------------+------------------------------------+ +| coszen | Cosine of zenith angle. | none | ++-------------------+-------------------------------------+------------------------------------+ +| fracsun | Daylight fraction of time | none | +| | interval. | | ++-------------------+-------------------------------------+------------------------------------+ +| co2 | Carbon dioxide concentration. | :math:`\text{ppmv}` | ++-------------------+-------------------------------------+------------------------------------+ +| lw_dtrans | Longwave (non-window) | none | +| | transmission. | | ++-------------------+-------------------------------------+------------------------------------+ +| lw_dtrans_win | | Longwave window transmission. | none | +| | | Note: only for ``'GEEN'`` scheme. | | ++-------------------+-------------------------------------+------------------------------------+ +| sw_dtrans | | Shortwave transmission. | none | +| | | Note: only for ``'GEEN'`` scheme. | | ++-------------------+-------------------------------------+------------------------------------+ + + + + +Relevant modules and subroutines +-------------------------------- + +Modules relevant to this one include: + +:astronomy_mod: Module that performs astronomical calcuations used for insolation. +:mixed_layer_mod: Surface albedo is set here. This is also where the surface temperature is updated. +:surface_flux_mod: An internal heat flux for giant planets can be set here. +:constants_mod: Planetary rotation rate and orbital period are set here. These are used in the calculations made by ``astronomy_mod``. + + +Other radiative transfer schemes are included in the following modules: + +:rrtm_radiation: Correlated-:math:`k` scheme tuned for Earth-like applications. +:socrates_interface_mod: Interface for flexible Met-Office correlated-:math:`k` scheme used for Earth-like and exoplanetary atmospheres. + + +References +---------- + +| [Byrne2013]_ +| [Frierson2006a]_ +| [Geen2016]_ +| [Schneider2009]_ +| [VallisEtAl2018]_ + + +Authors +---------- +This documentation was written by Neil Lewis, peer reviewed by Ruth Geen, and quality controlled by Matthew Henry. + diff --git a/docs/source/other_model_structure_diagram.png b/docs/source/other_model_structure_diagram.png new file mode 100644 index 000000000..a6003f924 Binary files /dev/null and b/docs/source/other_model_structure_diagram.png differ diff --git a/docs/source/python.rst b/docs/source/python.rst new file mode 100644 index 000000000..0efd40f37 --- /dev/null +++ b/docs/source/python.rst @@ -0,0 +1,4 @@ +Python API +========== + +Coming soon... diff --git a/docs/source/references.rst b/docs/source/references.rst new file mode 100644 index 000000000..f0f9e6206 --- /dev/null +++ b/docs/source/references.rst @@ -0,0 +1,140 @@ +References +========== + +.. [Alexander1999] Alexander, M. J., & Dunkerton, T. J. (1999). A spectral parameterization of + mean-flow forcing due to breaking gravity waves. Journal of the Atmospheric Sciences, + 56(24), 4167-4182. 2.0.CO;2>`_. + +.. [Betts1986] Betts, A. K., 1986: + A new convective adjustment scheme. Part I: Observational and theoretical basis. + *Quarterly Journal of the Royal Meteorological Society*, + **112(473)**, 677--691, + doi: `10.1002/qj.49711247307 `_. + +.. [BettsMiller1986] Betts, A. K. and Miller, M. J., 1986: + A new convective adjustment scheme. Part II: Single column tests using GATE wave, BOMEX, ATEX and arctic air-mass data sets. + *Quarterly Journal of the Royal Meteorological Society*, + **112(473)**, 693--709, + doi: `10.1002/qj.49711247308 `_. + +.. [Byrne2013] Byrne, M. P. and O’Gorman, P. A., 2013: Land–ocean warming contrast over a wide range of climates: Convective quasi-equilibrium the- ory and idealized simulations, *J. Climate*, **26**, doi. + +.. [Frierson2006a] Frierson, D. M. W., Held, I. M., and Zurita-Gotor, P., 2006: A Gray Radiation Aquaplanet Moist GCM, Part + 1: Static Stability and Eddy Scales, *J. Atmos. Sci.*, **63**, 2548–2566, doi. + +.. [Frierson2007] Frierson, D, 2007: + The Dynamics of Idealized Convection Schemes and Their Effect on the Zonally Averaged Tropical Circulation. + *Journal of the Atmospheric Sciences*, + **64(6)**, 1959--1976, + doi: `10.1175/JAS3935.1 `_. + +.. [Geen2016] Geen, R., Czaja, A., and Haigh, J. D., 2016: The effects of increasing humidity on heat transport by + extratropical waves, *Geophys. Res. Lett.*, **43**, 8314–8321, doi. + +.. [Gierasch2000] Gierasch, P.J., Ingersoll, A.P., Banfield, D., Ewald, S.P., Helfenstein, P., Simon-Miller, A., Vasavada, A., Breneman, H.H., Senske, D.A. and Team, G.I., 2000: + Observation of moist convection in Jupiter's atmosphere. + *Nature*, + **403(6770)**, 628--630. + doi: `10.1038/35001017 `_. + +.. [Jucker2017] Jucker, M. and Gerber, E. P., 2017: + Untangling the Annual Cycle of the Tropical Tropopause Layer with an Idealized Moist Model. + *Journal of Climate*, + **30(18)**, 7339--7358, + doi: `10.1175/JCLI-D-17-0127.1 `_. + +.. [OGormanSchneider2008] O'Gorman, P. and Schneider, T., 2008: + The Hydrological Cycle over a Wide Range of Climates Simulated with an Idealized GCM. + *Journal of Climate*, + **21(15)**, 3815--3832, + doi: `10.1175/2007JCLI2065.1 `_. + +.. [Large2004] Large, W. G., and Yeager, S., 2004: + Diurnal to decadal global forcing for ocean and sea-ice models: The data sets and flux climatologies. + *NCAR Technical Report; University Corporation for Atmospheric Research*, + **No. NCAR/TN-460+STR**, + doi: `10.5065/D6KK98Q6 `_. + +.. [Manabe1969] Manabe, S. 1969: + Climate and the Ocean Circulation: I. The Atmopsheric Circulation and the Hydrology of the Earth's Surface. + *Mon. Wea. Rev.* + **97(11)**, 739--774, + doi: 10.1175/1520-0493(1969)097<0739:CATOC>2.3.CO;2 + +.. [MannersEtAl2015] Manners, J. and Edwards, J. M. and Hill, P. and Thelen, J.-C., 2015: + SOCRATES (Suite Of Community RAdiative Transfer codes based on Edwards and Slingo) Technical Guide. + Met Office, UK. Available at: https://code.metoffice.gov.uk/trac/socrates. + +.. [MlawerEtAl1997] Mlawer, E. J. and Taubman, S. J. and Brown, P. D. and Iacono, M. J. and & Clough, S. A., 1997: + Radiative transfer for inhomogeneous atmospheres: RRTM, a validated correlated-k model for the longwave. + *Journal of Geophysical Research*, + **102(D14)** 16663--16682, + doi: `10.1029/97JD00237 `_. + +.. [Moorthi1992] Moorthi, S., Suarez, M. J., 1992: + Relaxed Arakawa-Schubert. A Parameterization of Moist Convection for General Circulation Models. + *Monthly Weather Review*, + **120(6)**, 978--1002, + doi: 10.1175/1520-0493(1992)120<0978:RASAPO>2.0.CO;2 + +.. [Pierrehumbert1986] Pierrehumbert, R. T. (1986, September). An essay on the parameterization + of orographic gravity wave drag. In Proc. Seminar/Workshop on Observation, Theory and + Modeling of Orographic Effects (Vol. 1, pp. 251-282). + +.. [Ricchiazzi1998] Ricchiazzi, P., Yang, S., Gautier, C., and Sowle, D., 1998: SBDART: A research and teaching + software tool for plane-parallel radiative transfer in the Earth’s atmosphere, + *B. Am. Meteorol. Soc.*, **79**, 2101–2114, doi. + +.. [Sauliere2012] Saulière, J., Brayshaw, D. J., Hoskins, B., & Blackburn, M. (2012): + Further investigation of the impact of idealized continents and SST distributions on the Northern Hemisphere storm tracks. + *J. Atmos. Sci.*, + **69(3)**, 840-856, + doi: `10.1175/JAS-D-11-0113.1 `_. + +.. [Schneider2006] Schneider, T., Walker, C. C., 2006: + Self-Organization of Atmospheric Macroturbulence into Critical States of Weak Nonlinear Eddy–Eddy Interactions. + *J. Atmos. Sci.*, + **63(6)**, 1569--1586, + doi: `10.1175/JAS3699.1 `_. + +.. [Schneider2009] Schneider, T. and Liu, J., 2009: Formation of jets and equatorial superrotation on Jupiter, + *J. Atmos. Sci.*, **66**, 579–601, doi. + +.. [Thomson_and_Vallis2019] Thomson, S. I. and Vallis, G. K., 2019. Hierarchical Modeling of Solar System + Planets with Isca. *Atmosphere*, **10 (12)**, 803, + doi: `10.3390/atmos10120803 `_. + +.. [NealeHoskins2004] Neale, R. B. and Hoskins, B. K., 2000. A standard test for AGCMs including their physical parametrizations: I: the proposal + *Atmospheric Science Letters*, + **1(2)**, 101-107 + +.. [MerlisEtAl2013] Merlis, Timothy M., et al. 2013. Hadley circulation response to orbital precession. Part I: Aquaplanets. + *Journal of Climate*, + **26(3)**, 740-753 + +.. [RusselEtAl1985] Russell, Gary L., James R. Miller, and Lie-Ching Tsang. 1985. Seasonal oceanic heat transports computed from an atmospheric model. + *Dynamics of atmospheres and Oceans* + **9(3)**, 253-271. + +.. [Vallis2017] Vallis, G. K., 2017: **Atmospheric and Oceanic Fluid Dynamics. Fundamentals and + Large-Scale Circulation**. 2nd ed. Cambridge University Press. + doi: `10.1017/9781107588417 `_. + +.. [VallisEtAl2018] Vallis, G. K. and Colyer, G. and Geen, R. and Gerber, E. and Jucker, M. and + Maher, P. and Paterson, A. and Pietschnig, M. and Penn, J. and Thomson, S. I., 2018: + Isca, v1.0: a framework for the global modelling of the atmospheres of Earth and + other planets at varying levels of complexity. *Geoscientific Model Development*, + **11(3)**, 843-859, + doi: `10.5194/gmd-11-843-2018 `_. + +.. [Williams2011] Williams, P. D., 2011: + The RAW Filter: An Improvement to the Robert-Asselin Filter in Semi-Implicit Integrations. + *Mon. Wea. Rev.* + **139(6)**, 1996–-2007, + doi: `10.1175/2010MWR3601.1 `_. + +.. [YanoPlant2016] Yano, J. and Plant, R., 2016: + Generalized convective quasi-equilibrium principle. + *Dynamics of Atmospheres and Oceans*, + **73**, 10--33, + doi: `10.1016/j.dynatmoce.2015.11.001 `_. diff --git a/docs/source/remote_access.rst b/docs/source/remote_access.rst new file mode 100644 index 000000000..97bb090e2 --- /dev/null +++ b/docs/source/remote_access.rst @@ -0,0 +1,88 @@ +Remote Access +============= + +Overview +-------- +This is a guide for how to edit remote files on a local text editor via port- +forwarding. By the end you should know: + +* how to quickly login to your remote server using an SSH config file +* how to edit remote files in a local text editor through ``rmate`` +* how to edit remote python files in a local ``jupyter`` environment + +Simplifying logins and port-forwarding +------------------------------------- +If you use a Unix-based operating system on your personal computer, you can make use +of an SSH config file to create shortcuts to your frequently used remote computers. We will take this one step further to simplify port-forwarding, a method which allows a user to redirect data from a specified remote host and port, through a secure tunnel, to a specified local port. Port-forwarding is helpful because it will enable us to edit remote files locally. + +As an example without any fancy tricks, let's set up an SSH tunnel that maps ``localhost`` port 3039 on my local machine to 3039 on my remote machine (the number is arbitrary as long as its between 1024 and 49150): ``$ ssh -l localhost:3039:host:3039 user@host``. You will then be required to enter in your password. This is cumbersome to repeat everytime we log in. Our goal will be to shorten the command to : ``$ ssh hostalias`` and without having to enter in your password. We give some instructions below: + +Set up an SSH config file +^^^^^^^^^^^^^^^^^^^^^^^^^ +In the home directory of your **local machine**, create a new directory called ``.ssh`` if it does not already exist. Navigate to this directory and create file called ``config``. +Put in the following contents (making sure to replace the text surrounded by ``**double asterisks**`` with your own information):: + + Host **hostalias** + Hostname **hostname** + User **username** + +By using an SSH config file, secure methods for copying (e.g. ``scp`` or ``sftp``) can now use the same host aliases. Let's say I want to copy a file from my local machine to my remove machine. This is now as simple as: ``$ scp localfile.txt hostalias:/path/to/directory``. + +Set up an SSH key pair +^^^^^^^^^^^^^^^^^^^^^^ +If your local machine is a Mac, you can eliminate the need to enter a password every time you want to log in by using an SSH key pair. To do this, on your **local machine** navigate to your ``~/.ssh`` directory and enter the following command to generate a set of RSA keys: ``$ ssh-keygen -t rsa``. You will then be prompted to supply a filename and a password. For the file name I recommend ``id_rsa_hostalias`` and for the password I recommend it to be the same as your remote machine's password. If you use a Mac, the operating system can use its internal keychain to remember your password, meaning you won't need to type it in every time you log in! + +This command will generate 2 files:the one with the ``.pub`` extension is the "public key", the one without the ``.pub`` extension is the "private key". You keep your private key strictly on your local machine. It is a good idea to change the permissions of the private key file. You need to copy your public key to the remote machine you would like to use the key pair to log in to. First create the ``~/.ssh`` on your **remote machine**. Then from your **local machine** enter: ``$ scp ~/.ssh/id_rsa_hostalias.pub hostalias:~/.ssh/``. After copying the public key over to ``hostalias``, you need to create an ``authorized_keys`` file in your ``~/.ssh/`` on your **remote machine**. + +Now from a terminal window on your **local machine**, you can try logging in to ``hostalias`` with the following command: ``$ ssh -i ~/.ssh/id_rsa_hostalias hostalias``. This will prompt you for the password you specified upon creating your key pair using ``ssh-keygen``. To always make use of your private key when logging in to ``hostalias``, add the following to your ``config`` file on your **local machine**:: + + Hostname **hostname** + User **username** + LocalForward 3039 localhost:3039 + IdentityFile **~/.ssh/id_rsa_hostname** + UseKeychain yes + AddKeysToAgent Yes + +You should not be able to log in simply by typing ``$ ssh hostalias``. Congratulations! + + + + +Edit remote files in a local text editor using ``rmate`` +-------------------------------------------------------- +As an alternative to remote-based text editors such as ``vi`` and ``emacs``, we can +use port-forwarding to set up a `local-based text editor like Atom `_ which includes features such as syntax highlighting and code completion. For instructions to install ``rmate`` on your **local machine**, `click here `_. Then to specifically use Atom to edit remote files, `click here `_. You will need to add the following line to your ``~/.ssh/config`` file: ``RemoteForward 52698 localhost:52698``. + + +Edit remote python files in a ``jupyter`` environment +----------------------------------------------------- +The ``jupyter`` environment is a great environment for data exploration and integrating +your figures inline with your code. To open your first Jupyter notebook, log in to your **remote machine** and type: ``$ jupyter lab --no-browser --port=3039``. + +To make it even quicker, you can type the following on your **local machine**: ``$ssh remotehost "jupyter lab --no-browser --port=3039``. This should function because of all the work we put in during the port forwarding section. To shorten this command, add an alias to your ``~/.bashrc`` file on your **local machine**. I personally use the alias ``rjlab``. + +Recap +----- +Your final ``~/.ssh/config`` file should look like this (making sure to replace the text surrounded by ``**double asterisks**`` with your own information):: + + Host **hostalias** + Hostname **hostname** + User **username** + + Hostname **hostname** + User **username** + LocalForward 3039 localhost:3039 + RemoteForward 52698 localhost:52698 + IdentityFile **~/.ssh/id_rsa_hostname** + UseKeychain yes + AddKeysToAgent Yes + +**Final Notes:** + +* Do not use this with VPN, use ithome aka hashbang as proxy. If the connection is interrupted you can still reconnect, assuming the jupyter process is still running. But make sure not to leave zombie jupyter processes with open ports on remote hosts! + +* Remember the port numbers chosen are arbitrary. If you choose the same number as someone else on your network, their files may open up on your computer and vice versa! + +Authors +------- +This documentation was written by Brett McKim, peer reviewed by Denis Sergeev, and quality controlled by Ross Castle. diff --git a/docs/source/testing/index.rst b/docs/source/testing/index.rst new file mode 100644 index 000000000..c07efb6ac --- /dev/null +++ b/docs/source/testing/index.rst @@ -0,0 +1,10 @@ +.. _testing: + +################## +Testing Isca +################## + +.. toctree:: + :maxdepth: 1 + + trip_test diff --git a/docs/source/testing/trip_test.rst b/docs/source/testing/trip_test.rst new file mode 100644 index 000000000..14febaef3 --- /dev/null +++ b/docs/source/testing/trip_test.rst @@ -0,0 +1,123 @@ +Trip Tests +============== + +Summary +------- +The primary purpose of the trip tests is to identify if proposed changes to the Isca master result in it giving different results for a range of test cases. + +The trip test program takes two commit IDs as input, and compares the results of Isca's test cases using the two versions of Isca that are defined by the commit IDs. A comparison is then made between the output from each of the test cases. The output of the program shows which test cases produced bit-wise identical results and which did not. + +**Please note** that this guide assumes some knowledge of the `git` version control system. If you get stuck on this, there are lots of free guides online to using `git`, e.g. `GitHub's Git Handbook `_. + +Why do we ask you to run the trip tests? +----------------------------------------- + +We are very keen for people to contribute their modifications and enhancements back into Isca's master - for instructions on how to contribute, see the :doc:`../contributing` . As part of this process, before we accept your modifications, we require you to run the trip tests. By running these tests, we can check whether your modifications changes the results of any of the test cases. + +We want to check this for three principal reasons: + + 1. If you add a new feature, we want to make sure it is turned off by default. This is so that an existing Isca user who doesn't know about the feature isn't forced to use it when they upgrade to the newest Isca version. This prevents confusion where two versions of Isca give different answers for the same experimental configuration. + 2. If your modification contains a bug fix, then this will likely alter Isca's output. If this is the case, we want to know which of test cases are affected by your bug fix. + 3. You may think your modifications won't have changed Isca's results, but we want to make sure that's the case. The trip tests are very good at highlighting unexpected consequences of your changes, and help us keep to a high-standard of reproducibility. + + +How do I run the trip tests? +--------------------------------- +The first thing to do before you run the trip tests is to decide on which two commit IDs you would like to compare, and then obtain their commit IDs. When you submit a pull request, we ask you to use the current HEAD commit of Isca's master branch as one of the commits, and the HEAD of your pull-requested branch as the other. If you are using the trip tests as a testing tool, then you can choose any two commits you like. + +To run the trip tests, navigate to ``${GFDL_BASE}/exp/test_cases/trip_test/``. From this directory the trip tests are run using the command-line interface. The simplest example call is +:: + ./trip_test_command_line 155661f ec29bf3 + +This would run all of the test cases, comparing the results using commit ID ``155661f`` with commit ID ``ec29bf3``. These commit IDs are for illustration purposes only. Please use two commit IDs relevant for your testing (i.e. the current HEAD of the Isca master and the HEAD of your branch). + +The optional arguments and their defaults are +:: + -e 'all' - Runs all test experiments + -n 4 - Uses 4 cores to run Isca + -r 'git@github.com:ExeClim/Isca' - Uses the online Isca repo to checkout commits from + +To specify a subset of the test cases, use the ``-e`` option +:: + ./trip_test_command_line 155661f ec29bf3 -e 'axisymmetric' 'bucket_model' 'frierson' + +**Please note** that when you submit a pull request, you need to show results for *all* the test cases. Even if you think your modifications will not affect another test case, the trip tests are an easy way of making sure. For nearly all pull requests, we will require all the trip tests to pass. See :ref:`FAQs`. The option for running a subset of test cases is intended for subsequent testing if, for example, you find only one test fails, and you want to spend more focussed time investigating which commit caused the failure. + +To change the number of cores Isca is run on, use the ``-n`` option. +:: + ./trip_test_command_line 155661f ec29bf3 -e 'axisymmetric' 'bucket_model' 'frierson' -n 4 + +To specify the github repo used for the tests (e.g. your fork rather than ``ExeClim/Isca``), use the ``-r`` option +:: + ./trip_test_command_line 155661f ec29bf3 -e 'axisymmetric' 'bucket_model' 'frierson' -r git@github.com:YOUR_GITHUB_USERNAME/Isca + +**Please note that by default the trip tests will try to connect to the ExeClim/Isca repository using SSH**. If you use HTTPs please use the option ``-r https://github.com/ExeClim/Isca.git``. + +How do I meet the trip test requirements for a pull request? +----------------------------------------------------------------------- + +1. Identify the commit ID of the HEAD of Isca's master branch, and the HEAD of your pull-requested branch. +2. Run the trip test for all of the test cases (default) using these two commit IDs. +3. Post the summary output in your pull request (see below for example summary output). +4. Mark your pull request with the label ``trip tests passing`` if all of the tests pass. Mark your pull request with ``changes previous results`` if some of the trip tests fail. + +If the tests all pass then your pull request should be accepted. If some of the tests fail then *please investigate why they have failed*. In general we will require all the tests to pass for a pull request to be accepted. If, however, you have fixed a bug and this causes a test case to fail, or similarly you think the results should change, then please make your case for this in the pull request. + +**Please note** that you are required to run the trip tests when submitting a pull request that changes Isca's model code. Updates to the python front-end will be excluded from this requirement. + + +Example output +-------------- + +Running the command +:: + ./trip_test_command_line 155661f ec29bf3 + +Produces the following summary output +:: + Results for all of the test cases ran comparing 155661f and ec29bf3 are as follows... + variable_co2_grey : pass + realistic_continents_fixed_sst : pass + realistic_continents_variable_qflux : pass + held_suarez : pass + variable_co2_rrtm : pass + bucket_model : fail + top_down_test : pass + frierson : pass + axisymmetric : pass + MiMA : pass + giant_planet : pass + Nightmare, some tests have failed + +The bucket test is the only one that fails in this instance. This is because the bucket model formulation was changed between these two commits, and so it is expected that the results with the two commits will differ. However, we note that the other tests have their results unchanged, meaning this is a safe modification to the code, and the pull request is likely to be approved. Any unexpected failures should be investigated before submitting a pull-request. + +.. _FAQs: + +FAQs +---- + +* **What happens if the trip tests fail for my modifications?** If some of the trip tests fail, please investigate why you think this might be. If the tests fail on an isolated test case that you haven't used before, please post the results in your pull request and we can discuss what is best to do. We will accept some pull requests that fail the trip tests, but we will need to understand why first. + +* **When I submit a pull request, which two commit IDs should I use?** Please use the commit ID of the current HEAD of Isca's master branch as one, and the HEAD of your pull-requested branch as the other. *Please note that in order for this to work, you will have to use the* `-r` *option to select your own fork, as described above.* + +* **How do I find a commit ID?** The commit ID is a 40-character string of letters and numbers associated with the commit, which you can find on GitHub under `Commits` or using `git log` on the command line. You require only the first 7 characters of the ID for these tests. + +* **How long do the trip tests take to run?** The code is re-compiled several times during the trip tests, so the majority of time will be spent compiling. Each test case is only run for a few days, so once the code has compiled it shouldn't take more than 30 minutes or so, depending on how many cores you're using. You can increase the number of cores by using the ``-n`` option. + +* **I am getting an error message saying that git cannot find my commit ID - how do I fix this?** If you are specifying commit IDs that are present on your fork but not in the ExeClim version of Isca, you will need to specify your own fork as the repository. To do this, use the ``-r`` option, e.g. ``-r git@github.com:YOUR_GITHUB_USERNAME/Isca``. Note that you can specify the repository location using the HTTPS option, or the SSH option depending on your preference. + +* **Do I have to connect to GitHub to run the trip tests, or can I do it all locally?** It is possible to run the trip tests locally. Use the option ``-r PATH_TO_MY_LOCAL_REPO`` and it should work fine. + +* **Which of the test cases does the trip tests run**? A list of the available test cases can be found in the ``list_all_test_cases_implemented_in_trip_test`` function in ``${GFDL_BASE}/exp/test_cases/trip_test/trip_test_functions.py``. + +* **I haven't setup Socrates - does that matter?** Socrates is an optional radiation scheme that Isca can be complied with, but is not by default (see :doc:`../modules/socrates`). So you are not required to run the Socrates test case if you have not set it up - just post the results you can obtain. + +* **If I add a new test case to the** ``test_cases`` **folder, how do I add it to the trip tests?** In the ``get_nml_diag`` function in ``trip_test_functions.py``, you will see several similar calls to import the namelist dictionary from each of the existing test cases. Just copy this syntax and edit it for your test case. You can then add the name of your test case to the list in the ``list_all_test_cases_implemented_in_trip_test`` function, and it should be run by default. *Please note* that if you add a new trip test that uses new features, it will fail the trip test because the existing Isca master won't be able to run it. This is OK, and you should highlight this when submitting the pull request. + +* **I have defined a new** ``codebase`` **object for my test case. How can I get the trip test to select it?** In the ``conduct_comparison_on_test_case`` the codebase object is selected. You can edit this section accordingly. + +* **Why don't you compare the results of the test cases to some standard output, rather than comparing the results from two commits?** We do this because different compilers and different hardware can produce slightly different results for a given test case. Therefore comparing to standard output is a much more difficult test for users to pass. We therefore compare the results of two different Isca versions *on the same hardware and with the same compilers* to make the tests easier to understand. + +Authors +---------- +This documentation was written by Stephen Thomson, peer reviewed by Penelope Maher, and quality controlled by Ross Castle. \ No newline at end of file diff --git a/exp/site_specific/umd/ReadMe.md b/exp/site_specific/umd/ReadMe.md new file mode 100644 index 000000000..28b70eca4 --- /dev/null +++ b/exp/site_specific/umd/ReadMe.md @@ -0,0 +1 @@ +To compile Isca on the University of Maryland's Deepthought2 supercomputer, simply follow the instructions in the main ReadMe.md, but use `export GFDL_ENV=deepthought2`. diff --git a/exp/test_cases/MiMA/MiMA_test_case.py b/exp/test_cases/MiMA/MiMA_test_case.py index 270596744..586211935 100644 --- a/exp/test_cases/MiMA/MiMA_test_case.py +++ b/exp/test_cases/MiMA/MiMA_test_case.py @@ -19,8 +19,6 @@ # is used to load the correct compilers. The env file is always loaded from # $GFDL_BASE and not the checked out git repo. -cb.compile() # compile the source code to working directory $GFDL_WORK/codebase - # create an Experiment object to handle the configuration of model parameters # and output diagnostics exp = Experiment('mima_test_experiment', codebase=cb) @@ -174,6 +172,9 @@ }) #Lets do a run! if __name__=="__main__": + + cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + exp.run(1, use_restart=False, num_cores=NCORES) for i in range(2,121): exp.run(i, num_cores=NCORES) diff --git a/exp/test_cases/README.md b/exp/test_cases/README.md index fd1d07016..56b9bd6f0 100644 --- a/exp/test_cases/README.md +++ b/exp/test_cases/README.md @@ -6,6 +6,9 @@ `bucket_hydrology` * As described in Isca paper (Vallis et al., 2017) but without q-fluxes +`column_test_case` +* Single column configuration of Isca. Please cite McKim et al. (2024, submitted) () if you use the SCM. + `frierson` * Control case of the so-called `Frierson model` described in e.g. diff --git a/exp/test_cases/ape_aquaplanet/socrates_ape_aquaplanet_T42.py b/exp/test_cases/ape_aquaplanet/socrates_ape_aquaplanet_T42.py new file mode 100644 index 000000000..1df4b3633 --- /dev/null +++ b/exp/test_cases/ape_aquaplanet/socrates_ape_aquaplanet_T42.py @@ -0,0 +1,231 @@ +import os +import numpy as np +from isca import SocratesCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE + +NCORES = 16 #32 is max for gv3 +base_dir = os.path.dirname(os.path.realpath(__file__)) +# a CodeBase can be a directory on the computer, +# useful for iterative development +cb = SocratesCodeBase.from_directory(GFDL_BASE) + +# or it can point to a specific git repo and commit id. +# This method should ensure future, independent, reproducibility of results. +# cb = DryCodeBase.from_repo(repo='https://github.com/isca/isca', commit='isca1.1') + +# compilation depends on computer specific settings. The $GFDL_ENV +# environment variable is used to determine which `$GFDL_BASE/src/extra/env` file +# is used to load the correct compilers. The env file is always loaded from +# $GFDL_BASE and not the checked out git repo. + +# create an Experiment object to handle the configuration of model parameters +# and output diagnostics + +exp = Experiment('ape_aqua_soc_low_res', codebase=cb) +exp.clear_rundir() + +inputfiles = [os.path.join(GFDL_BASE,'input/rrtm_input_files/ozone_1990.nc')] + +#Tell model how to write diagnostics +diag = DiagTable() +diag.add_file('atmos_monthly', 30, 'days', time_units='days') +diag.add_file('atmos_daily', 1, 'days', time_units='days') + +#when not an aquaplanet, you need to interpolate vertically! +diag.add_field('dynamics', 'ps', time_avg=True) #ps +diag.add_field('dynamics', 'bk') +diag.add_field('dynamics', 'pk') +diag.add_field('dynamics', 'zsurf', time_avg=True) + +diag.add_field('mixed_layer', 't_surf', files=['atmos_monthly'], time_avg=True) #ts +diag.add_field('mixed_layer', 'flux_t', time_avg=True) #hfls +diag.add_field('mixed_layer', 'flux_lhe', time_avg=True) #hfss - LH is evap if / L_v +diag.add_field('mixed_layer', 'albedo', files=['atmos_monthly'], time_avg=True) + +diag.add_field('atmosphere', 'precipitation', time_avg=True) #pr +diag.add_field('atmosphere', 'condensation_rain', time_avg=True) #pr-pc +diag.add_field('atmosphere', 'rh', files=['atmos_monthly'], time_avg=True) #hur + +#surface wind stress +diag.add_field('atmosphere', 'flux_u', files=['atmos_monthly'], time_avg=True) #tauu - zonal component of stress +diag.add_field('atmosphere', 'flux_v', files=['atmos_monthly'], time_avg=True) #tauv + +#near surface properties +diag.add_field('atmosphere', 'temp_2m', time_avg=True) +diag.add_field('atmosphere', 'sphum_2m', time_avg=True) +diag.add_field('atmosphere', 'u_10m', files=['atmos_monthly'], time_avg=True) +diag.add_field('atmosphere', 'v_10m', files=['atmos_monthly'], time_avg=True) + +#common variables +diag.add_field('dynamics', 'sphum', time_avg=True) #hus +diag.add_field('dynamics', 'ucomp', time_avg=True) #ua +diag.add_field('dynamics', 'vcomp', time_avg=True) #va +diag.add_field('dynamics', 'omega', time_avg=True) #wap +diag.add_field('dynamics', 'temp', time_avg=True) #ta +diag.add_field('dynamics', 'height', time_avg=True) #zg + +#radiative fluxes +diag.add_field('socrates', 'soc_surf_flux_lw', files=['atmos_monthly'], time_avg=True) #net +diag.add_field('socrates', 'soc_surf_flux_lw_down', files=['atmos_monthly'], time_avg=True) + +diag.add_field('socrates', 'soc_olr', files=['atmos_monthly'], time_avg=True) + +diag.add_field('socrates', 'soc_surf_flux_sw', files=['atmos_monthly'], time_avg=True) #net +diag.add_field('socrates', 'soc_surf_flux_sw_down', files=['atmos_monthly'], time_avg=True) + +diag.add_field('socrates', 'soc_toa_sw', files=['atmos_monthly'], time_avg=True) #net +diag.add_field('socrates', 'soc_toa_sw_down', files=['atmos_monthly'], time_avg=True) + +#tendencies +diag.add_field('socrates', 'soc_tdt_lw', files=['atmos_monthly'], time_avg=True) +diag.add_field('socrates', 'soc_tdt_sw', files=['atmos_monthly'], time_avg=True) +diag.add_field('socrates', 'soc_tdt_rad', files=['atmos_monthly'], time_avg=True) + +#needed for eddy flux terms +diag.add_field('dynamics', 'ucomp_vcomp', files=['atmos_monthly'], time_avg=True) +diag.add_field('dynamics', 'sphum_v', files=['atmos_monthly'], time_avg=True) +diag.add_field('dynamics', 'vcomp_temp', files=['atmos_monthly'], time_avg=True) + +exp.diag_table = diag +exp.inputfiles = inputfiles + +#Define values for the 'core' namelist +exp.namelist = namelist = Namelist({ + 'main_nml':{ + 'days' : 30, + 'hours' : 0, + 'minutes': 0, + 'seconds': 0, + 'dt_atmos':720, + 'current_date' : [1,1,1,0,0,0], + 'calendar' : 'thirty_day' + }, + + 'socrates_rad_nml': { + 'stellar_constant':1365., #solar constant incoming + 'lw_spectral_filename':os.path.join(GFDL_BASE,'src/atmos_param/socrates/src/trunk/data/spectra/ga7/sp_lw_ga7'), + 'sw_spectral_filename':os.path.join(GFDL_BASE,'src/atmos_param/socrates/src/trunk/data/spectra/ga7/sp_sw_ga7'), + 'do_read_ozone': True, + 'ozone_file_name':'ozone_1990', + 'ozone_field_name':'ozone_1990', + 'dt_rad':4320, + 'solday':90, #turn off seasonal cycle - diurnal by default + 'co2_ppmv':348.0, + 'store_intermediate_rad':True, + 'chunk_size': 16, + 'use_pressure_interp_for_half_levels':False, + 'tidally_locked':False + }, + + 'idealized_moist_phys_nml': { + 'do_damping': True, + 'turb':True, + 'mixed_layer_bc':True, + 'do_virtual' :False, + 'do_simple': True, + 'roughness_mom':3.21e-05, + 'roughness_heat':3.21e-05, + 'roughness_moist':3.21e-05, + 'two_stream_gray': False, #Use the grey radiation scheme + 'do_socrates_radiation': True, + 'convection_scheme': 'SIMPLE_BETTS_MILLER', #Use simple Betts miller convection + }, + + + 'vert_turb_driver_nml': { + 'do_mellor_yamada': False, # default: True + 'do_diffusivity': True, # default: False + 'do_simple': True, # default: False + 'constant_gust': 0.0, # default: 1.0 + 'use_tau': False + }, + + 'diffusivity_nml': { + 'do_entrain':False, + 'do_simple': True, + }, + + 'surface_flux_nml': { + 'use_virtual_temp': False, + 'do_simple': True, + 'old_dtaudv': True + }, + + 'atmosphere_nml': { + 'idealized_moist_model': True + }, + + #Use a large mixed-layer depth, and the Albedo of the CTRL case in Jucker & Gerber, 2017 + 'mixed_layer_nml': { + 'tconst' : 285., + 'prescribe_initial_dist':True, + 'evaporation':True, + 'depth': 10.0, #Depth of mixed layer used + 'albedo_value': 0.38, #Albedo value used + 'do_ape_sst': True, + }, + + 'qe_moist_convection_nml': { + 'rhbm':0.7, + 'Tmin':160., + 'Tmax':350. + }, + + 'lscale_cond_nml': { + 'do_simple':True, + 'do_evap':True + }, + + 'sat_vapor_pres_nml': { + 'do_simple':True, + 'construct_table_wrt_liq_and_ice':True + }, + + 'damping_driver_nml': { + 'do_rayleigh': True, + 'trayfric': -0.5, # neg. value: time in *days* + 'sponge_pbottom': 150., #Setting the lower pressure boundary for the model sponge layer in Pa. + 'do_conserve_energy': True, + }, + + # FMS Framework configuration + 'diag_manager_nml': { + 'mix_snapshot_average_fields': False # time avg fields are labelled with time in middle of window + }, + + 'fms_nml': { + 'domains_stack_size': 600000 # default: 0 + }, + + 'fms_io_nml': { + 'threading_write': 'single', # default: multi + 'fileset_write': 'single', # default: multi + }, + + 'spectral_dynamics_nml': { + 'damping_order': 4, + 'water_correction_limit': 200.e2, + 'reference_sea_level_press':1.0e5, + 'num_levels':40, #How many model pressure levels to use + 'valid_range_t':[100.,800.], + 'initial_sphum':[2.e-6], + 'vert_coord_option':'uneven_sigma', + 'surf_res':0.2, #Parameter that sets the vertical distribution of sigma levels + 'scale_heights' : 11.0, + 'exponent':7.0, + 'robert_coeff':0.03 + }, + +}) + +#Lets do a run! +if __name__=="__main__": + + cb.compile(debug=False) + exp.set_resolution('T42') + exp.run(1, use_restart=False, num_cores=NCORES, overwrite_data=True)#, run_idb=True) + + #for i in range(2,385): #all runs should be 30 years + spin up + # exp.run(i, num_cores=NCORES, overwrite_data=True) + + + diff --git a/exp/test_cases/axisymmetric/axisymmetric_test_case.py b/exp/test_cases/axisymmetric/axisymmetric_test_case.py index f2a6afba2..54884a989 100644 --- a/exp/test_cases/axisymmetric/axisymmetric_test_case.py +++ b/exp/test_cases/axisymmetric/axisymmetric_test_case.py @@ -19,8 +19,6 @@ # is used to load the correct compilers. The env file is always loaded from # $GFDL_BASE and not the checked out git repo. -cb.compile() # compile the source code to working directory $GFDL_WORK/codebase - # create an Experiment object to handle the configuration of model parameters # and output diagnostics exp = Experiment('axisymmetric_test_case', codebase=cb) @@ -141,7 +139,8 @@ 'rrtm_radiation_nml': { 'do_read_ozone':True, 'ozone_file':'ozone_1990', - 'dt_rad': 3600. #Set RRTM radiation timestep to 3600 seconds, meaning it runs every 5 atmospheric timesteps + 'dt_rad': 3600, #Set RRTM radiation timestep to 3600 seconds, meaning it runs every 5 atmospheric timesteps + }, # FMS Framework configuration @@ -181,6 +180,9 @@ #Lets do a run! if __name__=="__main__": + + cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + exp.run(1, use_restart=False, num_cores=NCORES) for i in range(2,121): - exp.run(i, num_cores=NCORES) \ No newline at end of file + exp.run(i, num_cores=NCORES) diff --git a/exp/test_cases/barotropic_vorticity_equation/barotropic_vor_eq_stirring_test.py b/exp/test_cases/barotropic_vorticity_equation/barotropic_vor_eq_stirring_test.py new file mode 100644 index 000000000..ffe74e7c8 --- /dev/null +++ b/exp/test_cases/barotropic_vorticity_equation/barotropic_vor_eq_stirring_test.py @@ -0,0 +1,117 @@ +import os + +import numpy as np + +from isca import BarotropicCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE + +NCORES = 8 +base_dir = os.path.dirname(os.path.realpath(__file__)) +# a CodeBase can be a directory on the computer, +# useful for iterative development +cb = BarotropicCodeBase.from_directory(GFDL_BASE) + +# or it can point to a specific git repo and commit id. +# This method should ensure future, independent, reproducibility of results. +# cb = DryCodeBase.from_repo(repo='https://github.com/isca/isca', commit='isca1.1') + +# compilation depends on computer specific settings. The $GFDL_ENV +# environment variable is used to determine which `$GFDL_BASE/src/extra/env` file +# is used to load the correct compilers. The env file is always loaded from +# $GFDL_BASE and not the checked out git repo. + +# create an Experiment object to handle the configuration of model parameters +# and output diagnostics +exp = Experiment('barotropic_stirring_test_experiment', codebase=cb) + +#Tell model how to write diagnostics +diag = DiagTable() +diag.add_file('atmos_monthly', 30, 'days', time_units='days') + +#Tell model which diagnostics to write +diag.add_field('barotropic_diagnostics', 'ucomp', time_avg=True) +diag.add_field('barotropic_diagnostics', 'vcomp', time_avg=True) +diag.add_field('barotropic_diagnostics', 'vor', time_avg=True) +diag.add_field('barotropic_diagnostics', 'pv', time_avg=True) +diag.add_field('barotropic_diagnostics', 'stream', time_avg=True) +diag.add_field('barotropic_diagnostics', 'trs', time_avg=True) +diag.add_field('barotropic_diagnostics', 'tr', time_avg=True) +diag.add_field('barotropic_diagnostics', 'eddy_vor', time_avg=True) +diag.add_field('barotropic_diagnostics', 'delta_u', time_avg=True) +diag.add_field('stirring_mod', 'stirring', time_avg=True) +diag.add_field('stirring_mod', 'stirring_amp', time_avg=True) +diag.add_field('stirring_mod', 'stirring_sqr', time_avg=True) + +exp.diag_table = diag + +#Empty the run directory ready to run +exp.clear_rundir() + +#Define values for the 'core' namelist +exp.namelist = namelist = Namelist({ + 'main_nml':{ + 'days' : 30, + 'hours' : 0, + 'minutes': 0, + 'seconds': 0, + 'dt_atmos': 1200, + 'calendar': 'no_calendar', + }, + + 'atmosphere_nml':{ + 'print_interval': 86400, + }, + +'fms_io_nml':{ + 'threading_write' :'single', + 'fileset_write': 'single' + }, + + 'fms_nml':{ + 'print_memory_usage':True, + 'domains_stack_size': 200000, + }, + + 'barotropic_dynamics_nml':{ + 'triang_trunc' : True, + 'num_lat' : 128, + 'num_lon' : 256, + 'num_fourier' : 85, + 'num_spherical' : 86, + 'fourier_inc' : 1, + 'damping_option' : 'resolution_dependent', + 'damping_order' : 2, + 'damping_coeff' : 1.157E-4, + 'damping_coeff_r': 1.929E-6, + 'grid_tracer' : True, + 'spec_tracer' : True, + 'm_0' : 6, + 'zeta_0' : 0.0, + 'eddy_lat' : 45.0, + 'eddy_width' : 10.0, + 'robert_coeff' : 0.04, + 'initial_zonal_wind' : 'zero', + }, + + 'barotropic_physics_nml':{ + }, + + 'stirring_nml': { + 'decay_time':172800, + 'amplitude':3.e-11, + 'lat0':45., + 'lon0':180., + 'widthy':12., + 'widthx':45., + 'B':1.0, + }, + +}) + +#Lets do a run! +if __name__=="__main__": + + cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + + exp.run(1, use_restart=False, num_cores=NCORES) + for i in range(2,121): + exp.run(i, num_cores=NCORES) diff --git a/exp/test_cases/barotropic_vorticity_equation/barotropic_vor_eq_test.py b/exp/test_cases/barotropic_vorticity_equation/barotropic_vor_eq_test.py new file mode 100644 index 000000000..11c7cce62 --- /dev/null +++ b/exp/test_cases/barotropic_vorticity_equation/barotropic_vor_eq_test.py @@ -0,0 +1,101 @@ +import os + +import numpy as np + +from isca import BarotropicCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE + +NCORES = 8 +base_dir = os.path.dirname(os.path.realpath(__file__)) +# a CodeBase can be a directory on the computer, +# useful for iterative development +cb = BarotropicCodeBase.from_directory(GFDL_BASE) + +# or it can point to a specific git repo and commit id. +# This method should ensure future, independent, reproducibility of results. +# cb = DryCodeBase.from_repo(repo='https://github.com/isca/isca', commit='isca1.1') + +# compilation depends on computer specific settings. The $GFDL_ENV +# environment variable is used to determine which `$GFDL_BASE/src/extra/env` file +# is used to load the correct compilers. The env file is always loaded from +# $GFDL_BASE and not the checked out git repo. + +# create an Experiment object to handle the configuration of model parameters +# and output diagnostics +exp = Experiment('barotropic_test_experiment', codebase=cb) + +#Tell model how to write diagnostics +diag = DiagTable() +diag.add_file('atmos_monthly', 30, 'days', time_units='days') + +#Tell model which diagnostics to write +diag.add_field('barotropic_diagnostics', 'ucomp', time_avg=True) +diag.add_field('barotropic_diagnostics', 'vcomp', time_avg=True) +diag.add_field('barotropic_diagnostics', 'vor', time_avg=True) +diag.add_field('barotropic_diagnostics', 'pv', time_avg=True) +diag.add_field('barotropic_diagnostics', 'stream', time_avg=True) +diag.add_field('barotropic_diagnostics', 'trs', time_avg=True) +diag.add_field('barotropic_diagnostics', 'tr', time_avg=True) +diag.add_field('barotropic_diagnostics', 'eddy_vor', time_avg=True) +diag.add_field('barotropic_diagnostics', 'delta_u', time_avg=True) + +exp.diag_table = diag + +#Empty the run directory ready to run +exp.clear_rundir() + +#Define values for the 'core' namelist +exp.namelist = namelist = Namelist({ + 'main_nml':{ + 'days' : 30, + 'hours' : 0, + 'minutes': 0, + 'seconds': 0, + 'dt_atmos': 1200, + 'calendar': 'no_calendar', + }, + + 'atmosphere_nml':{ + 'print_interval': 86400, + }, + +'fms_io_nml':{ + 'threading_write' :'single', + 'fileset_write': 'single' + }, + + 'fms_nml':{ + 'print_memory_usage':True, + 'domains_stack_size': 200000, + }, + + 'barotropic_dynamics_nml':{ + 'triang_trunc' : True, + 'num_lat' : 128, + 'num_lon' : 256, + 'num_fourier' : 85, + 'num_spherical' : 86, + 'fourier_inc' : 1, + 'damping_option' : 'resolution_dependent', + 'damping_order' : 4, + 'damping_coeff' : 1.e-04, + 'grid_tracer' : True, + 'spec_tracer' : True, + 'm_0' : 4, + 'zeta_0' : 8.e-05, + 'eddy_lat' : 45.0, + 'eddy_width' : 15.0, + 'robert_coeff' : 0.04, + }, + + 'barotropic_physics_nml':{ + }, +}) + +#Lets do a run! +if __name__=="__main__": + + cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + + exp.run(1, use_restart=False, num_cores=NCORES) + for i in range(2,121): + exp.run(i, num_cores=NCORES) diff --git a/exp/test_cases/bucket_hydrology/bucket_model_test_case.py b/exp/test_cases/bucket_hydrology/bucket_model_test_case.py index 73431552a..85f10bb17 100644 --- a/exp/test_cases/bucket_hydrology/bucket_model_test_case.py +++ b/exp/test_cases/bucket_hydrology/bucket_model_test_case.py @@ -19,8 +19,6 @@ # is used to load the correct compilers. The env file is always loaded from # $GFDL_BASE and not the checked out git repo. -cb.compile() # compile the source code to working directory $GFDL_WORK/codebase - # create an Experiment object to handle the configuration of model parameters # and output diagnostics exp = Experiment('bucket_test_experiment', codebase=cb) @@ -179,6 +177,8 @@ #Lets do a run! if __name__=="__main__": + cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + exp.run(1, use_restart=False, num_cores=NCORES) for i in range(2,121): exp.run(i, num_cores=NCORES) \ No newline at end of file diff --git a/exp/test_cases/column_test_case/column_test.py b/exp/test_cases/column_test_case/column_test.py new file mode 100644 index 000000000..32e655260 --- /dev/null +++ b/exp/test_cases/column_test_case/column_test.py @@ -0,0 +1,202 @@ +""" +This script configures a column model that uses Isca's columnwise physics routines. + +Single column configuration of Isca. Please cite McKim et al. (2024, submitted) (https://doi.org/10.22541/essoar.170904795.55675140/v1) if you use the SCM. + +Useful for testing new convection / radiation parametrizations, as the dynamical core is +bypassed so the model runs a gazillion times faster (especially if you're only simulating +one column). Can in principle simulate many (in lat and lon) at the same time. + +The wind is prescribed (it needs to be non-zero at the surface to allow for latent and +sensible surface heat fluxes). Currently the user can set a namelist variable 'surface_wind' +that sets u_surf and v_surf = surface_wind / sqrt(2), so that wind_surf = sqrt(u_surf**2 + +v_surf**2) = surface_wind. u and v at all other altitudes are set to zero (hardcoded). + +At the moment the model needs to use the vertical turbulent diffusion parameterization in order +for the mixed layer code to work. This is not very consistent as the u and v wind are prescribed +and so the u,v tendenency from the diffusion is thrown away. Hence an implicit assumption when +using the column model is that 'the dynamics' would restore the surface winds to their prescribed +speed, so that du/dt total is zero. + +The column model is currently initiated as a bit of a hack. The line + +'from isca import ColumnCodeBase' + +sets a compiler flag -DCOLUMN_MODEL that tells the model to use the following files: + +atmos_column/column.F90 +atmos_column/column_grid.F90 +atmos_column/column_init_cond.F90 +atmos_column/column_initialize_fields.F90 + +to initialize the model (including constructing the model grid), do the model timestepping +(using a leapfrog scheme as before), and handle input/output. + +Works with either hs_forcing, or the physics packages in idealized_moist_phys. Even when +multiple columns are simulated, the model can only run on 1 core at the moment (will endeavour +to fix this as some point). Also, the column model cannot read in topography input files. + +Any questions to Neil Lewis: +n.t.lewis@exeter.ac.uk +""" + + +import os + +import numpy as np + +from isca import ColumnCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE + + +# column model only uses 1 core +NCORES = 1 + +# compile code +base_dir = os.path.dirname(os.path.realpath(__file__)) +cb = ColumnCodeBase.from_directory(GFDL_BASE) +cb.compile() + +# create an Experiment object to handle the configuration of model parameters +exp = Experiment('column_test_exp', codebase=cb) + +#Tell model how to write diagnostics +diag = DiagTable() +diag.add_file('atmos_monthly', 30, 'days', time_units='days') + +#Tell model which diagnostics to write +diag.add_field('column', 'ps', time_avg=True) +diag.add_field('column', 'bk') +diag.add_field('column', 'pk') +diag.add_field('atmosphere', 'precipitation', time_avg=True) +diag.add_field('mixed_layer', 't_surf', time_avg=True) +diag.add_field('mixed_layer', 'flux_lhe', time_avg=True) +diag.add_field('column', 'sphum', time_avg=True) +diag.add_field('column', 'ucomp', time_avg=True) +diag.add_field('column', 'vcomp', time_avg=True) +diag.add_field('column', 'temp', time_avg=True) +diag.add_field('two_stream', 'swdn_toa', time_avg=True) +diag.add_field('atmosphere', 'dt_ug_diffusion', time_avg=True) +diag.add_field('atmosphere', 'dt_vg_diffusion', time_avg=True) +exp.diag_table = diag + +#Empty the run directory ready to run +exp.clear_rundir() + +#Define values for the 'core' namelist +exp.namelist = namelist = Namelist({ + 'main_nml':{ + 'days' : 360, + 'hours' : 0, + 'minutes': 0, + 'seconds': 0, + 'dt_atmos':1440, + 'current_date' : [1,1,1,0,0,0], + 'calendar' : 'thirty_day' + }, + + 'atmosphere_nml': { + 'idealized_moist_model': True + }, + + 'column_nml': { + 'lon_max': 1, # number of columns in longitude, default begins at lon=0.0 + 'lat_max': 1, # number of columns in latitude, precise + # latitude can be set in column_grid_nml if only 1 lat used. + 'num_levels': 31, # number of levels + 'initial_sphum': 1e-3, + 'q_decrease_only':True, # constrain q in stratosphere + }, + + 'column_grid_nml': { + 'lat_value': np.rad2deg(np.arcsin(1/np.sqrt(3))) # set latitude to that which causes insolation in frierson p2 radiation to be insolation / 4. + #'global_average': True # don't use this option at the moment + }, + + # set initial condition, NOTE: currently there is not an option to read in initial condition from a file (aside from a restart file). + 'column_init_cond_nml': { + 'initial_temperature': 264., # initial atmospheric temperature + 'surf_geopotential': 0.0, # applied to all columns + 'surface_wind': 5. # as described above + }, + + 'idealized_moist_phys_nml': { + 'do_damping': False, # no damping in column model, surface wind prescribed + 'turb':True, + 'mixed_layer_bc':True, + 'do_simple': True, # simple RH calculation + 'roughness_mom': 3.21e-05, + 'roughness_heat':3.21e-05, + 'roughness_moist':3.21e-05, + 'two_stream_gray': True, #Use grey radiation + 'convection_scheme': 'SIMPLE_BETTS_MILLER', #Use the simple Betts Miller convection scheme + 'do_lcl_diffusivity_depth':True, # use convection scheme LCL height to set PBL depth + }, + + 'two_stream_gray_rad_nml': { + 'rad_scheme': 'frierson', #Select radiation scheme to use, which in this case is Frierson + 'do_seasonal': False, #do_seasonal=false uses the p2 insolation profile from Frierson 2006. do_seasonal=True uses the GFDL astronomy module to calculate seasonally-varying insolation. + 'atm_abs': 0.2, # default: 0.0 + }, + + 'qe_moist_convection_nml': { + 'rhbm':0.7, # rh criterion for convection + 'Tmin':160., # min temperature for convection scheme look up tables + 'Tmax':350. # max temperature for convection scheme look up tables + }, + + 'lscale_cond_nml': { + 'do_simple':True, # only rain + 'do_evap':False, # no re-evaporation of falling precipitation + }, + + 'surface_flux_nml': { + 'use_virtual_temp': True, # use virtual temperature for BL stability + 'do_simple': True, + 'old_dtaudv': True + }, + + 'vert_turb_driver_nml': { + 'do_mellor_yamada': False, # default: True + 'do_diffusivity': True, # default: False + 'do_simple': True, # default: False + 'constant_gust': 0.0, # default: 1.0 + 'use_tau': False + }, + + #Use a large mixed-layer depth, and the Albedo of the CTRL case in Jucker & Gerber, 2017 + 'mixed_layer_nml': { + 'tconst' : 285., + 'prescribe_initial_dist':False, + 'evaporation':True, + 'depth': 2.5, #Depth of mixed layer used + 'albedo_value': 0.30, #Albedo value used + }, + + 'sat_vapor_pres_nml': { + 'do_simple':True, + }, + + # define pressure coordinate + 'vert_coordinate_nml': { + 'bk': [0.000000, 0.0117665, 0.0196679, 0.0315244, 0.0485411, 0.0719344, 0.1027829, 0.1418581, 0.1894648, 0.2453219, 0.3085103, 0.3775033, 0.4502789, 0.5244989, 0.5977253, 0.6676441, 0.7322627, 0.7900587, 0.8400683, 0.8819111, 0.9157609, 0.9422770, 0.9625127, 0.9778177, 0.9897489, 1.0000000], + 'pk': [0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000], + }, + + # FMS Framework configuration + 'diag_manager_nml': { + 'mix_snapshot_average_fields': False # time avg fields are labelled with time in middle of window + }, + 'fms_nml': { + 'domains_stack_size': 600000 # default: 0 + }, + 'fms_io_nml': { + 'threading_write': 'single', # default: multi + 'fileset_write': 'single', # default: multi + }, +}) + +#Lets do a run! +if __name__=="__main__": + exp.run(1, use_restart=False, num_cores=NCORES, mpirun_opts='--bind-to socket') + for i in range(2,11): + exp.run(i, num_cores=NCORES, mpirun_opts='--bind-to socket') diff --git a/exp/test_cases/column_test_case/column_test_rrtm_ozone.py b/exp/test_cases/column_test_case/column_test_rrtm_ozone.py new file mode 100644 index 000000000..cffd83813 --- /dev/null +++ b/exp/test_cases/column_test_case/column_test_rrtm_ozone.py @@ -0,0 +1,223 @@ +""" +This script configures a column model that uses Isca's columnwise physics routines. + +Single column configuration of Isca. Please cite McKim et al. (2024, submitted) (https://doi.org/10.22541/essoar.170904795.55675140/v1) if you use the SCM. + +Useful for testing new convection / radiation parametrizations, as the dynamical core is +bypassed so the model runs a gazillion times faster (especially if you're only simulating +one column). Can in principle simulate many (in lat and lon) at the same time. + +The wind is prescribed (it needs to be non-zero at the surface to allow for latent and +sensible surface heat fluxes). Currently the user can set a namelist variable 'surface_wind' +that sets u_surf and v_surf = surface_wind / sqrt(2), so that wind_surf = sqrt(u_surf**2 + +v_surf**2) = surface_wind. u and v at all other altitudes are set to zero (hardcoded). + +At the moment the model needs to use the vertical turbulent diffusion parameterization in order +for the mixed layer code to work. This is not very consistent as the u and v wind are prescribed +and so the u,v tendenency from the diffusion is thrown away. Hence an implicit assumption when +using the column model is that 'the dynamics' would restore the surface winds to their prescribed +speed, so that du/dt total is zero. + +The column model is currently initiated as a bit of a hack. The line + +'from isca import ColumnCodeBase' + +sets a compiler flag -DCOLUMN_MODEL that tells the model to use the following files: + +atmos_column/column.F90 +atmos_column/column_grid.F90 +atmos_column/column_init_cond.F90 +atmos_column/column_initialize_fields.F90 + +to initialize the model (including constructing the model grid), do the model timestepping +(using a leapfrog scheme as before), and handle input/output. + +Works with either hs_forcing, or the physics packages in idealized_moist_phys. Even when +multiple columns are simulated, the model can only run on 1 core at the moment (will endeavour +to fix this as some point). Also, the column model cannot read in topography input files. + +Any questions to Neil Lewis: +n.t.lewis@exeter.ac.uk +""" + + +import os + +import numpy as np + +from isca import ColumnCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE + +### to create ozone file: +from scm_interp_routine import scm_interp, global_average_lat_lon + +# column model only uses 1 core +NCORES = 1 + +# compile code +base_dir = os.path.dirname(os.path.realpath(__file__)) +cb = ColumnCodeBase.from_directory(GFDL_BASE) +cb.compile() + +# create an Experiment object to handle the configuration of model parameters +exp = Experiment('column_test_rrtm_ozone', codebase=cb) + + +#Tell model how to write diagnostics +diag = DiagTable() +diag.add_file('atmos_monthly', 30, 'days', time_units='days') + +#Tell model which diagnostics to write +diag.add_field('column', 'ps', time_avg=True) +diag.add_field('column', 'bk') +diag.add_field('column', 'pk') +diag.add_field('atmosphere', 'precipitation', time_avg=True) +diag.add_field('mixed_layer', 't_surf', time_avg=True) +diag.add_field('mixed_layer', 'flux_lhe', time_avg=True) +diag.add_field('column', 'sphum', time_avg=True) +diag.add_field('column', 'ucomp', time_avg=True) +diag.add_field('column', 'vcomp', time_avg=True) +diag.add_field('column', 'temp', time_avg=True) +diag.add_field('rrtm_radiation', 'toa_sw', time_avg=True) +diag.add_field('rrtm_radiation', 'olr', time_avg=True) +diag.add_field('rrtm_radiation', 'coszen', time_avg=True) +diag.add_field('rrtm_radiation', 'ozone', time_avg=True) +diag.add_field('atmosphere', 'dt_ug_diffusion', time_avg=True) +diag.add_field('atmosphere', 'dt_vg_diffusion', time_avg=True) +exp.diag_table = diag + +#Empty the run directory ready to run +exp.clear_rundir() + +#Define values for the 'core' namelist +exp.namelist = namelist = Namelist({ + 'main_nml':{ + 'days' : 360, + 'hours' : 0, + 'minutes': 0, + 'seconds': 0, + 'dt_atmos':7200, + 'current_date' : [1,1,1,0,0,0], + 'calendar' : 'thirty_day' + }, + + 'atmosphere_nml': { + 'idealized_moist_model': True + }, + + 'column_nml': { + 'lon_max': 1, # number of columns in longitude, default begins at lon=0.0 + 'lat_max': 1, # number of columns in latitude, precise + # latitude can be set in column_grid_nml if only 1 lat used. + 'num_levels': 50, # number of levels + 'initial_sphum': 1e-3, + 'vert_coord_option': 'even_sigma', + 'q_decrease_only':True # constrain q in stratosphere + }, + + 'column_grid_nml': { + #'lat_value': np.rad2deg(np.arcsin(1/np.sqrt(3))) # set latitude to that which causes insolation in frierson p2 radiation to be insolation / 4. + 'global_average': True # don't use this option at the moment + }, + + # set initial condition, NOTE: currently there is not an option to read in initial condition from a file (aside from a restart file). + 'column_init_cond_nml': { + 'initial_temperature': 264., # initial atmospheric temperature + 'surf_geopotential': 0.0, # applied to all columns + 'surface_wind': 5. # as described above + }, + + 'idealized_moist_phys_nml': { + 'do_damping': False, # no damping in column model, surface wind prescribed + 'turb':True, + 'mixed_layer_bc':True, + 'do_simple': True, # simple RH calculation + 'roughness_mom': 3.21e-05, + 'roughness_heat':3.21e-05, + 'roughness_moist':3.21e-05, + 'two_stream_gray': False, #Use grey radiation + 'do_rrtm_radiation': True, + 'convection_scheme': 'SIMPLE_BETTS_MILLER', #Use the simple Betts Miller convection scheme + 'do_lcl_diffusivity_depth':True, # use convection scheme LCL height to set PBL depth + }, + + 'rrtm_radiation_nml': { + 'solr_cnst': 1360, #s set solar constant to 1360, rather than default of 1368.22 + 'dt_rad': 7200, #Use long RRTM timestep + 'do_rad_time_avg':True, + 'dt_rad_avg':86400, + 'co2ppmv':400., + }, + + 'qe_moist_convection_nml': { + 'rhbm':0.7, # rh criterion for convection + 'Tmin':160., # min temperature for convection scheme look up tables + 'Tmax':350. # max temperature for convection scheme look up tables + }, + + 'lscale_cond_nml': { + 'do_simple':True, # only rain + 'do_evap':False, # no re-evaporation of falling precipitation + }, + + 'surface_flux_nml': { + 'use_virtual_temp': True, # use virtual temperature for BL stability + 'do_simple': True, + 'old_dtaudv': True + }, + + 'vert_turb_driver_nml': { + 'do_mellor_yamada': False, # default: True + 'do_diffusivity': True, # default: False + 'do_simple': True, # default: False + 'constant_gust': 0.0, # default: 1.0 + 'use_tau': False + }, + + #Use a large mixed-layer depth, and the Albedo of the CTRL case in Jucker & Gerber, 2017 + 'mixed_layer_nml': { + 'tconst' : 285., + 'prescribe_initial_dist':False, + 'evaporation':True, + 'depth': 2.5, #Depth of mixed layer used + 'albedo_value': 0.20, #Albedo value used + }, + + 'sat_vapor_pres_nml': { + 'do_simple':True, + }, + + # FMS Framework configuration + 'diag_manager_nml': { + 'mix_snapshot_average_fields': False # time avg fields are labelled with time in middle of window + }, + 'fms_nml': { + 'domains_stack_size': 600000 # default: 0 + }, + 'fms_io_nml': { + 'threading_write': 'single', # default: multi + 'fileset_write': 'single', # default: multi + }, + + + 'astronomy_nml': { + 'ecc' : 0.0, + 'obliq' : 0.0, + 'per' : 0.0 + }, + +}) + +#Lets do a run! +if __name__=="__main__": + + + ds = scm_interp(filename=os.path.join(GFDL_BASE,'input/rrtm_input_files/ozone_1990.nc'), + varname='ozone_1990', + nlevels=50) + global_average_lat_lon(ds, 'ozone_1990_interp') + exp.namelist['rrtm_radiation_nml']['do_scm_ozone'] = True + exp.namelist['rrtm_radiation_nml']['scm_ozone'] = np.squeeze(ds.ozone_1990_interp_area_av.mean('time').values).tolist() + + exp.run(1, use_restart=False, num_cores=NCORES, mpirun_opts='--bind-to socket') + for i in range(2,11): + exp.run(i, num_cores=NCORES, mpirun_opts='--bind-to socket') diff --git a/exp/test_cases/column_test_case/column_test_socrates_ozone.py b/exp/test_cases/column_test_case/column_test_socrates_ozone.py new file mode 100644 index 000000000..fb6a9193b --- /dev/null +++ b/exp/test_cases/column_test_case/column_test_socrates_ozone.py @@ -0,0 +1,241 @@ +""" +This script configures a column model that uses Isca's columnwise physics routines. + +Single column configuration of Isca. Please cite McKim et al. (2024, submitted) (https://doi.org/10.22541/essoar.170904795.55675140/v1) if you use the SCM. + +Useful for testing new convection / radiation parametrizations, as the dynamical core is +bypassed so the model runs a gazillion times faster (especially if you're only simulating +one column). Can in principle simulate many (in lat and lon) at the same time. + +The wind is prescribed (it needs to be non-zero at the surface to allow for latent and +sensible surface heat fluxes). Currently the user can set a namelist variable 'surface_wind' +that sets u_surf and v_surf = surface_wind / sqrt(2), so that wind_surf = sqrt(u_surf**2 + +v_surf**2) = surface_wind. u and v at all other altitudes are set to zero (hardcoded). + +At the moment the model needs to use the vertical turbulent diffusion parameterization in order +for the mixed layer code to work. This is not very consistent as the u and v wind are prescribed +and so the u,v tendenency from the diffusion is thrown away. Hence an implicit assumption when +using the column model is that 'the dynamics' would restore the surface winds to their prescribed +speed, so that du/dt total is zero. + +The column model is currently initiated as a bit of a hack. The line + +'from isca import ColumnCodeBase' + +sets a compiler flag -DCOLUMN_MODEL that tells the model to use the following files: + +atmos_column/column.F90 +atmos_column/column_grid.F90 +atmos_column/column_init_cond.F90 +atmos_column/column_initialize_fields.F90 + +to initialize the model (including constructing the model grid), do the model timestepping +(using a leapfrog scheme as before), and handle input/output. + +Works with either hs_forcing, or the physics packages in idealized_moist_phys. Even when +multiple columns are simulated, the model can only run on 1 core at the moment (will endeavour +to fix this as some point). Also, the column model cannot read in topography input files. + +Any questions to Neil Lewis: +n.t.lewis@exeter.ac.uk +""" + + +import os + +import numpy as np + +from isca import SocColumnCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE + +### to create ozone file: +from scm_interp_routine import scm_interp, global_average_lat_lon + +# column model only uses 1 core +NCORES = 1 + +# compile code +base_dir = os.path.dirname(os.path.realpath(__file__)) +cb = SocColumnCodeBase.from_directory(GFDL_BASE) +cb.compile() + +# create an Experiment object to handle the configuration of model parameters +exp = Experiment('column_test_socrates_ozone', codebase=cb) + + +#Tell model how to write diagnostics +diag = DiagTable() +diag.add_file('atmos_monthly', 30, 'days', time_units='days') + +#Tell model which diagnostics to write +diag.add_field('column', 'ps', time_avg=True) +diag.add_field('column', 'bk') +diag.add_field('column', 'pk') +diag.add_field('atmosphere', 'precipitation', time_avg=True) +diag.add_field('mixed_layer', 't_surf', time_avg=True) +diag.add_field('mixed_layer', 'flux_lhe', time_avg=True) +diag.add_field('column', 'sphum', time_avg=True) +diag.add_field('column', 'ucomp', time_avg=True) +diag.add_field('column', 'vcomp', time_avg=True) +diag.add_field('column', 'temp', time_avg=True) +#radiative tendencies +diag.add_field('socrates', 'soc_tdt_lw', time_avg=True) +diag.add_field('socrates', 'soc_tdt_sw', time_avg=True) +diag.add_field('socrates', 'soc_tdt_rad', time_avg=True) + +#net (up) and down surface fluxes +diag.add_field('socrates', 'soc_surf_flux_lw', time_avg=True) +diag.add_field('socrates', 'soc_surf_flux_sw', time_avg=True) +diag.add_field('socrates', 'soc_surf_flux_lw_down', time_avg=True) +diag.add_field('socrates', 'soc_surf_flux_sw_down', time_avg=True) +#net (up) TOA and downard fluxes +diag.add_field('socrates', 'soc_olr', time_avg=True) +diag.add_field('socrates', 'soc_toa_sw', time_avg=True) +diag.add_field('socrates', 'soc_toa_sw_down', time_avg=True) +diag.add_field('atmosphere', 'dt_ug_diffusion', time_avg=True) +diag.add_field('atmosphere', 'dt_vg_diffusion', time_avg=True) +exp.diag_table = diag + +#Empty the run directory ready to run +exp.clear_rundir() + +#Define values for the 'core' namelist +exp.namelist = namelist = Namelist({ + 'main_nml':{ + 'days' : 360, + 'hours' : 0, + 'minutes': 0, + 'seconds': 0, + 'dt_atmos':7200, + 'current_date' : [1,1,1,0,0,0], + 'calendar' : 'thirty_day' + }, + + 'atmosphere_nml': { + 'idealized_moist_model': True + }, + + 'column_nml': { + 'lon_max': 1, # number of columns in longitude, default begins at lon=0.0 + 'lat_max': 1, # number of columns in latitude, precise + # latitude can be set in column_grid_nml if only 1 lat used. + 'num_levels': 50, # number of levels + 'initial_sphum': 1e-3, + 'vert_coord_option': 'even_sigma', + 'q_decrease_only':True, # constrain q in stratosphere + }, + + 'column_grid_nml': { + #'lat_value': np.rad2deg(np.arcsin(1/np.sqrt(3))) # set latitude to that which causes insolation in frierson p2 radiation to be insolation / 4. + 'global_average': True # don't use this option at the moment + }, + + # set initial condition, NOTE: currently there is not an option to read in initial condition from a file (aside from a restart file). + 'column_init_cond_nml': { + 'initial_temperature': 264., # initial atmospheric temperature + 'surf_geopotential': 0.0, # applied to all columns + 'surface_wind': 5. # as described above + }, + + 'idealized_moist_phys_nml': { + 'do_damping': False, # no damping in column model, surface wind prescribed + 'turb':True, + 'mixed_layer_bc':True, + 'do_simple': True, # simple RH calculation + 'roughness_mom': 3.21e-05, + 'roughness_heat':3.21e-05, + 'roughness_moist':3.21e-05, + 'two_stream_gray': False, #Use grey radiation + 'do_socrates_radiation': True, + 'convection_scheme': 'SIMPLE_BETTS_MILLER', #Use the simple Betts Miller convection scheme + 'do_lcl_diffusivity_depth':True, # use convection scheme LCL height to set PBL depth + }, + + + + 'socrates_rad_nml': { + 'stellar_constant':1370., + 'lw_spectral_filename':os.path.join(GFDL_BASE,'src/atmos_param/socrates/src/trunk/data/spectra/ga7/sp_lw_ga7'), + 'sw_spectral_filename':os.path.join(GFDL_BASE,'src/atmos_param/socrates/src/trunk/data/spectra/ga7/sp_sw_ga7'), + 'dt_rad':7200, + 'store_intermediate_rad':True, + 'chunk_size': 1, # MUST BE 1 FOR COLUMN MODEL + 'use_pressure_interp_for_half_levels':False, + 'tidally_locked':False, + 'do_rad_time_avg':True, + 'dt_rad_avg':86400, + #'solday': 90 + }, + + 'qe_moist_convection_nml': { + 'rhbm':0.7, # rh criterion for convection + 'Tmin':160., # min temperature for convection scheme look up tables + 'Tmax':350. # max temperature for convection scheme look up tables + }, + + 'lscale_cond_nml': { + 'do_simple':True, # only rain + 'do_evap':False, # no re-evaporation of falling precipitation + }, + + 'surface_flux_nml': { + 'use_virtual_temp': True, # use virtual temperature for BL stability + 'do_simple': True, + 'old_dtaudv': True + }, + + 'vert_turb_driver_nml': { + 'do_mellor_yamada': False, # default: True + 'do_diffusivity': True, # default: False + 'do_simple': True, # default: False + 'constant_gust': 0.0, # default: 1.0 + 'use_tau': False + }, + + #Use a large mixed-layer depth, and the Albedo of the CTRL case in Jucker & Gerber, 2017 + 'mixed_layer_nml': { + 'tconst' : 285., + 'prescribe_initial_dist':False, + 'evaporation':True, + 'depth': 2.5, #Depth of mixed layer used + 'albedo_value': 0.20, #Albedo value used + }, + + 'sat_vapor_pres_nml': { + 'do_simple':True, + }, + + # FMS Framework configuration + 'diag_manager_nml': { + 'mix_snapshot_average_fields': False # time avg fields are labelled with time in middle of window + }, + 'fms_nml': { + 'domains_stack_size': 600000 # default: 0 + }, + 'fms_io_nml': { + 'threading_write': 'single', # default: multi + 'fileset_write': 'single', # default: multi + }, + + + 'astronomy_nml': { + 'ecc' : 0.0, + 'obliq' : 0.0, + 'per' : 0.0 + }, + +}) + +#Lets do a run! +if __name__=="__main__": + + + ds = scm_interp(filename=os.path.join(GFDL_BASE,'input/rrtm_input_files/ozone_1990.nc'), + varname='ozone_1990', + nlevels=50) + global_average_lat_lon(ds, 'ozone_1990_interp') + exp.namelist['socrates_rad_nml']['do_scm_ozone'] = True + exp.namelist['socrates_rad_nml']['scm_ozone'] = np.squeeze(ds.ozone_1990_interp_area_av.mean('time').values).tolist() + + exp.run(1, use_restart=False, num_cores=NCORES, mpirun_opts='--bind-to socket') + for i in range(2,11): + exp.run(i, num_cores=NCORES, mpirun_opts='--bind-to socket') diff --git a/exp/test_cases/column_test_case/scm_interp_routine.py b/exp/test_cases/column_test_case/scm_interp_routine.py new file mode 100644 index 000000000..a82419e53 --- /dev/null +++ b/exp/test_cases/column_test_case/scm_interp_routine.py @@ -0,0 +1,242 @@ +import xarray as xr +import numpy as np +from isca import GFDL_BASE +import os + + + +def vinterp(data, vcoord, vlevels): + + """ vertical linear interpolation, credit ExeClim/ShareCode""" + + assert (vcoord.ndim == data.ndim or vcoord.ndim == 1 and data.ndim == 4 or + vcoord.ndim == 4 and data.ndim == 1) + if vcoord.ndim == 1 and data.ndim > 1: + # This handles the case where vcoord is 1D and data is N-D + v_dim = int(np.where(np.array(data.shape) == vcoord.shape[0])[0]) + # numpy.broadcast_to only works for the last axis of an array, + # swap our shape around so that vertical dimension is last, broadcast + # vcoord to it, then swap the axes back so vcoord.shape == data.shape + data_shape = list(data.shape) + data_shape[-1], data_shape[v_dim] = data_shape[v_dim], data_shape[-1] + + vcoord = np.broadcast_to(vcoord, data_shape) + vcoord = np.swapaxes(vcoord, -1, v_dim) + + vcoord_shape = list(vcoord.shape) + vcoord_shape.pop(1) + vcoord_shape = tuple(vcoord_shape) + + valid = np.min([np.prod(vcoord_shape) - + np.sum(np.isnan(vcoord[:, 0, ...])), + np.prod(vcoord_shape) - + np.sum(np.isnan(vcoord[:, -1, ...]))]) + + if np.sum(vcoord[:, 0, ...] > vcoord[:, -1, ...]) / valid > 0.80: + # Vcoord data is decreasing on interpolation axis, (at least 80% is) + idx_gt = 1 + idx_lt = 0 + else: + # Data is increasing on interpolation axis + idx_gt = 0 + idx_lt = 1 + + if data.ndim >= vcoord.ndim: + # Handle case where data has the same dimensions or data has more + # dimensions compared to vcoord (e.g. vcoord is 4D, data is 4D, + # or vcoord is 1D, data is 4D) + out_shape = list(data.shape) + else: + # Handle case where data has fewer dimensions than vcoord + # (e.g. data is 1-D vcoord is N-D) + out_shape = list(vcoord.shape) + out_shape[1] = vlevels.shape[0] + + out_shape = tuple(out_shape) + out_data = np.zeros(out_shape) + np.nan + + for lev_idx, lev in enumerate(vlevels): + if idx_gt == 0: + # Case where vcoord data is increasing, find index where + # vcoord below [:-1] is equal or less than desired lev, and + # vcoord above [1:] is greater than lev, this means for lev + # is between these points, use weight to determine exactly where + idx = np.where(np.logical_and(vcoord[:, :-1, ...] <= lev, + vcoord[:, 1:, ...] > lev)) + + else: + # This does the same, but where vcoord is decreasing with index, + # so find where vcoord below [:-1] is greater, and vcoord above + # [1:] is less or equal + idx = np.where(np.logical_and(vcoord[:, :-1, ...] > lev, + vcoord[:, 1:, ...] <= lev)) + # Reduce diminsions of `idx` + idx = np.squeeze(idx) + # Create copies of this index, so they can be modified for + # weighting functions and output array + idx_abve = idx.copy() + idx_belw = idx.copy() + out_idx = idx.copy() + + # The interpolation axis index (1) for output + # is the level index (lev_idx) + out_idx[1, :] = lev_idx + + # Weighting function 'above' is index +1 for decreasing, + # or index +0 for decr. + idx_abve[1, :] += idx_gt + # Weighting function 'below' is index +0 for decreasing, + # or index +1 for decr. + idx_belw[1, :] += idx_lt + + # Change indicies back into tuples so + # numpy.array.__getitem__ understands them + idx_abve = tuple(idx_abve) + idx_belw = tuple(idx_belw) + out_idx = tuple(out_idx) + + # Weighting function for distance above lev + wgt1 = ((lev - vcoord[idx_belw]) / + (vcoord[idx_abve] - vcoord[idx_belw])) + + # Weighting function for distance below lev + wgt0 = 1.0 - wgt1 + + if data.ndim >= vcoord.ndim: + # Handle case where data has same or more dimensions than vcoord + out_data[out_idx] = (wgt0 * data[idx_belw] + wgt1 * data[idx_abve]) + else: + # Handle case where data has fewer dimensions than vcoord + out_data[out_idx] = (wgt0 * data[idx_belw[1]] + + wgt1 * data[idx_abve[1]]) + + return np.squeeze(out_data) + +def global_average_lat_lon(ds_in, var_name, radius=6371.e3): + + try: + ds_in['area_array'] + except KeyError: + cell_area(ds_in, radius) + + + weighted_data = ds_in[var_name]*ds_in['area_array'] + + area_average = weighted_data.mean(('lat', 'lon')) / ds_in['area_array'].mean(('lat','lon')) + + var_in_dims = ds_in[var_name].dims + + var_out_dims = tuple(x for x in var_in_dims if x!='lat' and x!='lon') + + ds_in[var_name+'_area_av'] = (var_out_dims, area_average.data) + +def cell_area(dataset_in, radius = 6371.e3): + + lonb = dataset_in['lonb'] + latb = dataset_in['latb'] + + lonb_1 = lonb[1::].values + lonb_2 = lonb[0:-1].values + + delta_lon = lonb_1 - lonb_2 + + latb_1 = latb[1::].values + latb_2 = latb[0:-1].values + + delta_lat = latb_1 - latb_2 + + dataset_in['delta_lon'] = (('lon'), delta_lon) + dataset_in['delta_lat'] = (('lat'), delta_lat) + + dataset_in['latb_1'] = (('lat'), latb_1) + dataset_in['latb_2'] = (('lat'), latb_2) + + xsize = radius*np.absolute(np.deg2rad(dataset_in['delta_lon']))*(np.sin(np.deg2rad(dataset_in['latb_1']))-np.sin(np.deg2rad(dataset_in['latb_2']))) + ysize = radius + + area_array = xsize*ysize + + dataset_in['area_array'] = (('lat','lon'), area_array.transpose('lat','lon').data) + +def pkbk(coord_option, nlevels, surf_res=.1, exponent=2.5, scale_heights=4.): + + if coord_option == 'even_sigma': + pk = np.zeros(nlevels+1) + bk = np.zeros(nlevels+1) + for lvl in range(0, nlevels): + bk[lvl] = float(lvl) / float(nlevels) + bk[-1] = 1.0 + elif coord_option == 'uneven_sigma': + pk = np.zeros(nlevels+1) + bk = np.zeros(nlevels+1) + for lvl in range(0, nlevels): + zeta = (1. - (float(lvl)/float(nlevels))) + z = surf_res*zeta + (1. - surf_res)*(zeta**exponent) + bk[lvl] = np.exp(-z * scale_heights) + bk[-1] = 1.0 + bk[0] = 0.0 + else: + print('pkbk: '+coord_option+' is NOT a coordinate option supported by this script') + + return pk, bk + +def calc_pfull(pk, bk, psurf, diff_option): + + nhalflevels = len(pk) + phalf = np.zeros(nhalflevels) + ln_top_level_factor = -1.0 + + for lvl in range(0, nhalflevels): + phalf[lvl] = pk[lvl] + bk[lvl] * psurf + + lnphalf = np.zeros(nhalflevels) + lnpfull = np.zeros(nhalflevels-1) + if diff_option == 'simmons_and_burridge': + for lvl in range(1, nhalflevels): + lnphalf[lvl] = np.log(phalf[lvl]) + + for lvl in range(1, nhalflevels-1): + alpha = 1 - phalf[lvl]*(lnphalf[lvl+1] - lnphalf[lvl])/(phalf[lvl+1]-phalf[lvl]) + lnpfull[lvl] = lnphalf[lvl+1] - alpha + lnpfull[0] = lnphalf[1] + ln_top_level_factor + lnphalf[0] = 0.0 + pfull = np.exp(lnpfull) + else: + print('calc_pfull: '+diff_option+' is NOT a vertical differencing option supported by this script') + + + return pfull + +def scm_interp(filename, varname='ozone_1990', vcoord_option='even_sigma', nlevels=None, + vert_difference_option = 'simmons_and_burridge', psurf=1.e3, pk_input=None, bk_input=None): + + # psurf in hPa + + + ds = xr.open_dataset(filename, decode_times=False) + + if vcoord_option != 'input': + pk, bk = pkbk(vcoord_option, nlevels) + else: + pk = pk_input + bk = bk_input + + pfull = calc_pfull(pk, bk, psurf, vert_difference_option) + + + data = vinterp(ds[varname].values, ds.pfull.values, pfull) + + + ds.coords['pfull_new'] = pfull + ds[varname+'_interp'] = (('time', 'pfull_new', 'lat'), data) + + + return ds + +if __name__ == "__main__": + + scm_interp(filename=os.path.join(GFDL_BASE,'input/rrtm_input_files/ozone_1990.nc'), + varname='ozone_1990', + nlevels=31) + + \ No newline at end of file diff --git a/exp/test_cases/frierson/frierson_test_case.py b/exp/test_cases/frierson/frierson_test_case.py index 6dfa83b10..6be2e58c0 100644 --- a/exp/test_cases/frierson/frierson_test_case.py +++ b/exp/test_cases/frierson/frierson_test_case.py @@ -19,8 +19,6 @@ # is used to load the correct compilers. The env file is always loaded from # $GFDL_BASE and not the checked out git repo. -cb.compile() # compile the source code to working directory $GFDL_WORK/codebase - # create an Experiment object to handle the configuration of model parameters # and output diagnostics exp = Experiment('frierson_test_experiment', codebase=cb) @@ -173,6 +171,8 @@ #Lets do a run! if __name__=="__main__": + cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + exp.run(1, use_restart=False, num_cores=NCORES) for i in range(2,121): exp.run(i, num_cores=NCORES) diff --git a/exp/test_cases/giant_planet/giant_planet_test_case.py b/exp/test_cases/giant_planet/giant_planet_test_case.py index a828c7d9a..dfe625b81 100644 --- a/exp/test_cases/giant_planet/giant_planet_test_case.py +++ b/exp/test_cases/giant_planet/giant_planet_test_case.py @@ -4,7 +4,7 @@ from isca import IscaCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE -NCORES = 4 +NCORES = 8 # a CodeBase can be a directory on the computer, # useful for iterative development @@ -19,8 +19,6 @@ # is used to load the correct compilers. The env file is always loaded from # $GFDL_BASE and not the checked out git repo. -cb.compile() # compile the source code to working directory $GFDL_WORK/codebase - # create an Experiment object to handle the configuration of model parameters # and output diagnostics exp = Experiment('giant_planet_test_experiment', codebase=cb) @@ -140,7 +138,7 @@ }, 'fms_nml': { - 'domains_stack_size': 620000 #Setting size of stack available to model, which needs to be higher than the default when running at high spatial resolution + 'domains_stack_size': 6200000 #Setting size of stack available to model, this can be decreased when not running at a high resolution. }, 'fms_io_nml': { @@ -207,6 +205,8 @@ #Lets do a run! if __name__=="__main__": + cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + exp.run(1, use_restart=False, num_cores=NCORES) for i in range(2,121): exp.run(i, num_cores=NCORES) diff --git a/exp/test_cases/held_suarez/held_suarez_test_case.py b/exp/test_cases/held_suarez/held_suarez_test_case.py index 9ef5ca06a..fbab6baca 100644 --- a/exp/test_cases/held_suarez/held_suarez_test_case.py +++ b/exp/test_cases/held_suarez/held_suarez_test_case.py @@ -18,8 +18,6 @@ # is used to load the correct compilers. The env file is always loaded from # $GFDL_BASE and not the checked out git repo. -cb.compile() # compile the source code to working directory $GFDL_WORK/codebase - # create an Experiment object to handle the configuration of model parameters # and output diagnostics @@ -104,6 +102,9 @@ #Lets do a run! if __name__ == '__main__': + + cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + exp.run(1, num_cores=NCORES, use_restart=False) for i in range(2, 13): exp.run(i, num_cores=NCORES) # use the restart i-1 by default \ No newline at end of file diff --git a/exp/test_cases/isca_job.sh b/exp/test_cases/isca_job.sh index 147b8f24b..9208901f8 100644 --- a/exp/test_cases/isca_job.sh +++ b/exp/test_cases/isca_job.sh @@ -1,17 +1,20 @@ -#!/bin/sh -#PBS -V # export all environment variables to the batch job. -#PBS -d . # set working directory to . -#PBS -q ptq # submit to the parallel test queue -#PBS -l nodes=1:ppn=8 # nodes=number of nodes required. ppn=number of processors per node -#PBS -l walltime=0:30:00 # Maximum wall time for the job. -#PBS -A Research_Project-PROJECTNUMBER # research project to submit under. -#PBS -m e -M USER@exeter.ac.uk # email me at job completion - # This is an example of how to submit an experiment to the queue on the Isca supercomputer # in Exeter. USER and PROJECTNUMBER in the metadata above need to be # updated to appropriate values for your experiment. # For more information about running on the Exeter cluster, see the wiki -# here: https://wiki.exeter.ac.uk/display/ISCA/Isca+Wiki +# here: https://universityofexeteruk.sharepoint.com/sites/ExeterARC + +#!/bin/bash +#SBATCH --export=ALL # export all environment variables to the batch job +#SBATCH -D . # set working directory to . +#SBATCH -p pq # submit to the parallel queue +#SBATCH --time=03:00:00 # maximum walltime for the job +#SBATCH -A Research_Project-PROJECTNUMBER # research project to submit under +#SBATCH --nodes=1 # specify number of nodes +#SBATCH --ntasks-per-node=16 # specify number of processors per node +#SBATCH --mail-type=END # send email at job completion +#SBATCH --mail-user=USER@exeter.ac.uk # email address + module load Anaconda3 -source activate gfdl -python held_suarez/parameter_sweep.py +source activate isca_env +python held_suarez/held_suarez_test_case.py diff --git a/exp/test_cases/realistic_continents/input/era-spectral7_T42_64x128.out.nc b/exp/test_cases/realistic_continents/input/era-spectral7_T42_64x128.out.nc new file mode 100644 index 000000000..a85274e62 Binary files /dev/null and b/exp/test_cases/realistic_continents/input/era-spectral7_T42_64x128.out.nc differ diff --git a/exp/test_cases/realistic_continents/realistic_continents_fixed_sst_test_case.py b/exp/test_cases/realistic_continents/realistic_continents_fixed_sst_test_case.py index ef5b3d03e..23b039d2f 100644 --- a/exp/test_cases/realistic_continents/realistic_continents_fixed_sst_test_case.py +++ b/exp/test_cases/realistic_continents/realistic_continents_fixed_sst_test_case.py @@ -21,8 +21,6 @@ # is used to load the correct compilers. The env file is always loaded from # $GFDL_BASE and not the checked out git repo. -cb.compile() # compile the source code to working directory $GFDL_WORK/codebase - # create an Experiment object to handle the configuration of model parameters # and output diagnostics exp = Experiment('realistic_continents_fixed_sst_test_experiment', codebase=cb) @@ -72,6 +70,8 @@ #Lets do a run! if __name__=="__main__": + cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + exp.run(1, use_restart=False, num_cores=NCORES) for i in range(2,121): exp.run(i, num_cores=NCORES) diff --git a/exp/test_cases/realistic_continents/realistic_continents_variable_qflux_test_case.py b/exp/test_cases/realistic_continents/realistic_continents_variable_qflux_test_case.py index 522c9cf00..52d5e61d2 100644 --- a/exp/test_cases/realistic_continents/realistic_continents_variable_qflux_test_case.py +++ b/exp/test_cases/realistic_continents/realistic_continents_variable_qflux_test_case.py @@ -21,8 +21,6 @@ # is used to load the correct compilers. The env file is always loaded from # $GFDL_BASE and not the checked out git repo. -cb.compile() # compile the source code to working directory $GFDL_WORK/codebase - # create an Experiment object to handle the configuration of model parameters # and output diagnostics exp = Experiment('realistic_continents_qflux_test_experiment', codebase=cb) @@ -72,6 +70,8 @@ #Lets do a run! if __name__=="__main__": + cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + exp.run(1, use_restart=False, num_cores=NCORES) for i in range(2,121): exp.run(i, num_cores=NCORES) diff --git a/exp/test_cases/shallow_water/prescribed_ics_test.py b/exp/test_cases/shallow_water/prescribed_ics_test.py new file mode 100644 index 000000000..52a50f95a --- /dev/null +++ b/exp/test_cases/shallow_water/prescribed_ics_test.py @@ -0,0 +1,109 @@ +import os + +import numpy as np + +from isca import ShallowCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE + +NCORES = 8 +base_dir = os.path.dirname(os.path.realpath(__file__)) +# a CodeBase can be a directory on the computer, +# useful for iterative development +cb = ShallowCodeBase.from_directory(GFDL_BASE) + +# or it can point to a specific git repo and commit id. +# This method should ensure future, independent, reproducibility of results. +# cb = DryCodeBase.from_repo(repo='https://github.com/isca/isca', commit='isca1.1') + +# compilation depends on computer specific settings. The $GFDL_ENV +# environment variable is used to determine which `$GFDL_BASE/src/extra/env` file +# is used to load the correct compilers. The env file is always loaded from +# $GFDL_BASE and not the checked out git repo. + +input_files = [os.path.join(base_dir,'input/rostami_t85_jet_and_vortex_mk7.nc')] + +# create an Experiment object to handle the configuration of model parameters +# and output diagnostics +exp = Experiment('shallow_test_experiment_nc_init_cond_rostami_1_daily_t85_mk7', codebase=cb) + +#Tell model how to write diagnostics +diag = DiagTable() +diag.add_file('atmos_hourly', 1, 'hours', time_units='days') + +#Tell model which diagnostics to write +diag.add_field('shallow_diagnostics', 'ucomp', time_avg=True) +diag.add_field('shallow_diagnostics', 'vcomp', time_avg=True) +diag.add_field('shallow_diagnostics', 'vor', time_avg=True) +diag.add_field('shallow_diagnostics', 'div', time_avg=True) +diag.add_field('shallow_diagnostics', 'h', time_avg=True) +diag.add_field('shallow_diagnostics', 'pv', time_avg=True) +diag.add_field('shallow_diagnostics', 'stream', time_avg=True) +diag.add_field('shallow_diagnostics', 'trs', time_avg=True) +diag.add_field('shallow_diagnostics', 'tr', time_avg=True) + + +exp.diag_table = diag + +#Empty the run directory ready to run +exp.clear_rundir() + +#Define values for the 'core' namelist +exp.namelist = namelist = Namelist({ + 'main_nml':{ + 'days' : 1, + 'hours' : 0, + 'minutes': 0, + 'seconds': 0, + 'dt_atmos': 1200, + 'calendar': 'no_calendar', + }, + + 'atmosphere_nml':{ + 'print_interval': 86400, + }, + +'fms_io_nml':{ + 'threading_write' :'single', + 'fileset_write': 'single' + }, + + 'fms_nml':{ + 'print_memory_usage':True, + 'domains_stack_size': 200000, + }, + + 'shallow_dynamics_nml':{ + 'num_lon' : 256, + 'num_lat' : 128, + 'num_fourier' : 85, + 'num_spherical' : 86, + 'fourier_inc' : 1, + 'damping_option' : 'resolution_dependent', + 'damping_order' : 4, + 'damping_coeff' : 1.e-04, + 'h_0' : 1048576.0, + 'grid_tracer' : True, + 'spec_tracer' : True, + 'robert_coeff' : 0.04, + 'robert_coeff_tracer' : 0.04, + 'initial_condition_from_input_file':True, + 'init_cond_file':'rostami_t85_jet_and_vortex_mk7' + }, + + 'shallow_physics_nml':{ + 'fric_damp_time' : 0.0, + 'therm_damp_time' : 0.0, + }, + + 'constants_nml':{ + 'radius':55000e3, + 'omega': 1.6e-4 + } +}) + +#Lets do a run! +if __name__=="__main__": + cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + exp.inputfiles = input_files + exp.run(1, use_restart=False, num_cores=NCORES) + # for i in range(2,121): + # exp.run(i, num_cores=NCORES) diff --git a/exp/test_cases/shallow_water/shallow_water_stirring_test.py b/exp/test_cases/shallow_water/shallow_water_stirring_test.py new file mode 100644 index 000000000..2cf2d29dc --- /dev/null +++ b/exp/test_cases/shallow_water/shallow_water_stirring_test.py @@ -0,0 +1,126 @@ +import os + +import numpy as np + +from isca import ShallowCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE + +NCORES = 8 +base_dir = os.path.dirname(os.path.realpath(__file__)) +# a CodeBase can be a directory on the computer, +# useful for iterative development +cb = ShallowCodeBase.from_directory(GFDL_BASE) + +# or it can point to a specific git repo and commit id. +# This method should ensure future, independent, reproducibility of results. +# cb = DryCodeBase.from_repo(repo='https://github.com/isca/isca', commit='isca1.1') + +# compilation depends on computer specific settings. The $GFDL_ENV +# environment variable is used to determine which `$GFDL_BASE/src/extra/env` file +# is used to load the correct compilers. The env file is always loaded from +# $GFDL_BASE and not the checked out git repo. + +# create an Experiment object to handle the configuration of model parameters +# and output diagnostics +exp = Experiment('shallow_stirring_test_experiment', codebase=cb) + +#Tell model how to write diagnostics +diag = DiagTable() +diag.add_file('atmos_monthly', 30, 'days', time_units='days') + +#Tell model which diagnostics to write +diag.add_field('shallow_diagnostics', 'ucomp', time_avg=True) +diag.add_field('shallow_diagnostics', 'vcomp', time_avg=True) +diag.add_field('shallow_diagnostics', 'vor', time_avg=True) +diag.add_field('shallow_diagnostics', 'div', time_avg=True) +diag.add_field('shallow_diagnostics', 'h', time_avg=True) +diag.add_field('shallow_diagnostics', 'pv', time_avg=True) +diag.add_field('shallow_diagnostics', 'stream', time_avg=True) +diag.add_field('shallow_diagnostics', 'trs', time_avg=True) +diag.add_field('shallow_diagnostics', 'tr', time_avg=True) +diag.add_field('stirring_mod', 'stirring', time_avg=True) +diag.add_field('stirring_mod', 'stirring_amp', time_avg=True) +diag.add_field('stirring_mod', 'stirring_sqr', time_avg=True) + +exp.diag_table = diag + +#Empty the run directory ready to run +exp.clear_rundir() + +#Define values for the 'core' namelist +exp.namelist = namelist = Namelist({ + 'main_nml':{ + 'days' : 30, + 'hours' : 0, + 'minutes': 0, + 'seconds': 0, + 'dt_atmos': 1200, + 'calendar': 'no_calendar', + }, + + 'atmosphere_nml':{ + 'print_interval': 86400, + }, + +'fms_io_nml':{ + 'threading_write' :'single', + 'fileset_write': 'single' + }, + + 'fms_nml':{ + 'print_memory_usage':True, + 'domains_stack_size': 200000, + }, + + 'shallow_dynamics_nml':{ + 'num_lon' : 256, + 'num_lat' : 128, + 'num_fourier' : 85, + 'num_spherical' : 86, + 'fourier_inc' : 1, + 'damping_option' : 'resolution_dependent', + 'damping_order' : 4, + 'damping_coeff' : 1.e-04, + 'h_0' : 3.e04, + 'grid_tracer' : True, + 'spec_tracer' : True, + 'robert_coeff' : 0.04, + 'robert_coeff_tracer' : 0.04, + }, + + 'shallow_physics_nml':{ + 'fric_damp_time' : -50.0, + 'therm_damp_time' : -10.0, + 'del_h' : 2.e04, + 'h_0' : 3.e04, + 'h_amp' : 1.e05, + 'h_lon' : 90.0, + 'h_lat' : 25.0, + 'h_width' : 15.0, + 'itcz_width' : 4.0, + 'h_itcz' : 4.e04, + }, + +#The below stirring parameters are those of Vallis et al 2004 DOI: 10.1175/1520-0469(2004)061<0264:AMASDM>2.0.CO;2 +#They have a decorrelation time set by 'decay_time', chosen to be 2 days by default. The forcing is also localised in +#latitude and longitude, with the centre of the forcing set by lat0 and lon0, and the width of the gaussians set by +#'widthy and widthx'. B sets the variation in longitude, with ampx = 1 + B*exp(-xx/widthx**2) + + 'stirring_nml': { + 'decay_time':172800, + 'amplitude':3.e-13, + 'lat0':45., + 'lon0':180., + 'widthy':12., + 'widthx':45., + 'B':1.0, + }, + +}) + +#Lets do a run! +if __name__=="__main__": + cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + + exp.run(1, use_restart=False, num_cores=NCORES) + for i in range(2,122): + exp.run(i, num_cores=NCORES) diff --git a/exp/test_cases/shallow_water/shallow_water_test.py b/exp/test_cases/shallow_water/shallow_water_test.py new file mode 100644 index 000000000..7ba248a8f --- /dev/null +++ b/exp/test_cases/shallow_water/shallow_water_test.py @@ -0,0 +1,108 @@ +import os + +import numpy as np + +from isca import ShallowCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE + +NCORES = 8 +base_dir = os.path.dirname(os.path.realpath(__file__)) +# a CodeBase can be a directory on the computer, +# useful for iterative development +cb = ShallowCodeBase.from_directory(GFDL_BASE) + +# or it can point to a specific git repo and commit id. +# This method should ensure future, independent, reproducibility of results. +# cb = DryCodeBase.from_repo(repo='https://github.com/isca/isca', commit='isca1.1') + +# compilation depends on computer specific settings. The $GFDL_ENV +# environment variable is used to determine which `$GFDL_BASE/src/extra/env` file +# is used to load the correct compilers. The env file is always loaded from +# $GFDL_BASE and not the checked out git repo. + +# create an Experiment object to handle the configuration of model parameters +# and output diagnostics +exp = Experiment('shallow_test_experiment', codebase=cb) + +#Tell model how to write diagnostics +diag = DiagTable() +diag.add_file('atmos_monthly', 30, 'days', time_units='days') + +#Tell model which diagnostics to write +diag.add_field('shallow_diagnostics', 'ucomp', time_avg=True) +diag.add_field('shallow_diagnostics', 'vcomp', time_avg=True) +diag.add_field('shallow_diagnostics', 'vor', time_avg=True) +diag.add_field('shallow_diagnostics', 'div', time_avg=True) +diag.add_field('shallow_diagnostics', 'h', time_avg=True) +diag.add_field('shallow_diagnostics', 'pv', time_avg=True) +diag.add_field('shallow_diagnostics', 'stream', time_avg=True) +diag.add_field('shallow_diagnostics', 'trs', time_avg=True) +diag.add_field('shallow_diagnostics', 'tr', time_avg=True) + + +exp.diag_table = diag + +#Empty the run directory ready to run +exp.clear_rundir() + +#Define values for the 'core' namelist +exp.namelist = namelist = Namelist({ + 'main_nml':{ + 'days' : 30, + 'hours' : 0, + 'minutes': 0, + 'seconds': 0, + 'dt_atmos': 1200, + 'calendar': 'no_calendar', + }, + + 'atmosphere_nml':{ + 'print_interval': 86400, + }, + +'fms_io_nml':{ + 'threading_write' :'single', + 'fileset_write': 'single' + }, + + 'fms_nml':{ + 'print_memory_usage':True, + 'domains_stack_size': 200000, + }, + + 'shallow_dynamics_nml':{ + 'num_lon' : 256, + 'num_lat' : 128, + 'num_fourier' : 85, + 'num_spherical' : 86, + 'fourier_inc' : 1, + 'damping_option' : 'resolution_dependent', + 'damping_order' : 4, + 'damping_coeff' : 1.e-04, + 'h_0' : 3.e04, + 'grid_tracer' : True, + 'spec_tracer' : True, + 'robert_coeff' : 0.04, + 'robert_coeff_tracer' : 0.04, + }, + + 'shallow_physics_nml':{ + 'fric_damp_time' : -50.0, + 'therm_damp_time' : -10.0, + 'del_h' : 2.e04, + 'h_0' : 3.e04, + 'h_amp' : 1.e05, + 'h_lon' : 90.0, + 'h_lat' : 25.0, + 'h_width' : 15.0, + 'itcz_width' : 4.0, + 'h_itcz' : 4.e04, + }, +}) + +#Lets do a run! +if __name__=="__main__": + cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + + exp.run(1, use_restart=False, num_cores=NCORES) + for i in range(2,121): + exp.run(i, num_cores=NCORES) diff --git a/exp/test_cases/simple_clouds/input/era_land_t42_filtered.nc b/exp/test_cases/simple_clouds/input/era_land_t42_filtered.nc new file mode 100644 index 000000000..f4ec1d2c6 Binary files /dev/null and b/exp/test_cases/simple_clouds/input/era_land_t42_filtered.nc differ diff --git a/exp/test_cases/simple_clouds/input/siconc_clim_amip.nc b/exp/test_cases/simple_clouds/input/siconc_clim_amip.nc new file mode 120000 index 000000000..2b6bbfae9 --- /dev/null +++ b/exp/test_cases/simple_clouds/input/siconc_clim_amip.nc @@ -0,0 +1 @@ +../../realistic_continents/input/siconc_clim_amip.nc \ No newline at end of file diff --git a/exp/test_cases/simple_clouds/input/sst_clim_amip.nc b/exp/test_cases/simple_clouds/input/sst_clim_amip.nc new file mode 120000 index 000000000..fc0f86745 --- /dev/null +++ b/exp/test_cases/simple_clouds/input/sst_clim_amip.nc @@ -0,0 +1 @@ +../../realistic_continents/input/sst_clim_amip.nc \ No newline at end of file diff --git a/exp/test_cases/simple_clouds/socrates_simcloud.py b/exp/test_cases/simple_clouds/socrates_simcloud.py new file mode 100644 index 000000000..7fdaba0fa --- /dev/null +++ b/exp/test_cases/simple_clouds/socrates_simcloud.py @@ -0,0 +1,325 @@ +import os +import numpy as np +from isca import SocratesCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE + +NCORES = 16 +NUM_LEVELS = 25 + +base_dir = os.path.dirname(os.path.realpath(__file__)) +# a CodeBase can be a directory on the computer, +# useful for iterative development +cb = SocratesCodeBase.from_directory(GFDL_BASE) + +# or it can point to a specific git repo and commit id. +# This method should ensure future, independent, reproducibility of results. +# cb = DryCodeBase.from_repo(repo='https://github.com/isca/isca', commit='isca1.1') + +# compilation depends on computer specific settings. The $GFDL_ENV +# environment variable is used to determine which `$GFDL_BASE/src/extra/env` file +# is used to load the correct compilers. The env file is always loaded from +# $GFDL_BASE and not the checked out git repo. + +# create an Experiment object to handle the configuration of model parameters +# and output diagnostics +exp = Experiment('soc_realistic_continents_fixed_sst_with_linear_cld_scheme', codebase=cb) + +# Tell model how to write diagnostics +diag = DiagTable() +diag.add_file('atmos_monthly', 30, 'days', time_units='days') + +# Tell model which diagnostics to write + +# need at least ps, pk, bk and zsurf to do vertical interpolation onto plevels from sigma +diag.add_field('dynamics', 'ps', time_avg=True) +diag.add_field('dynamics', 'bk') +diag.add_field('dynamics', 'pk') +diag.add_field('dynamics', 'zsurf') + +diag.add_field('dynamics', 'sphum', time_avg=True) +diag.add_field('dynamics', 'ucomp', time_avg=True) +diag.add_field('dynamics', 'vcomp', time_avg=True) +diag.add_field('dynamics', 'omega', time_avg=True) +diag.add_field('dynamics', 'temp', time_avg=True) +diag.add_field('dynamics', 'vor', time_avg=True) +diag.add_field('dynamics', 'div', time_avg=True) + +diag.add_field('atmosphere', 'precipitation', time_avg=True) +diag.add_field('mixed_layer', 't_surf', time_avg=True) +diag.add_field('mixed_layer', 'ice_conc', time_avg=True) +diag.add_field('mixed_layer', 'flux_lhe', time_avg=True) +diag.add_field('mixed_layer', 'flux_t', time_avg=True) + +# all-sky radiation fluxes at TOA and surface +diag.add_field('socrates', 'soc_flux_lw', time_avg=True) +diag.add_field('socrates', 'soc_flux_sw', time_avg=True) +diag.add_field('socrates', 'soc_olr', time_avg=True) +diag.add_field('socrates', 'soc_toa_sw', time_avg=True) +diag.add_field('socrates', 'soc_toa_sw_up', time_avg=True) +diag.add_field('socrates', 'soc_toa_sw_down', time_avg=True) + +diag.add_field('socrates', 'soc_surf_flux_lw', time_avg=True) +diag.add_field('socrates', 'soc_surf_flux_sw', time_avg=True) +diag.add_field('socrates', 'soc_surf_flux_lw_down', time_avg=True) +diag.add_field('socrates', 'soc_surf_flux_sw_down', time_avg=True) + +# clear-sky radiation fluxes at TOA and surface +diag.add_field('socrates', 'soc_olr_clr', time_avg=True) +diag.add_field('socrates', 'soc_toa_sw_clr', time_avg=True) +diag.add_field('socrates', 'soc_toa_sw_up_clr', time_avg=True) +diag.add_field('socrates', 'soc_flux_lw_clr', time_avg=True) +diag.add_field('socrates', 'soc_flux_sw_clr', time_avg=True) + +diag.add_field('socrates', 'soc_surf_flux_lw_clr', time_avg=True) +diag.add_field('socrates', 'soc_surf_flux_sw_clr', time_avg=True) +diag.add_field('socrates', 'soc_surf_flux_lw_down_clr', time_avg=True) +diag.add_field('socrates', 'soc_surf_flux_sw_down_clr', time_avg=True) + +# Cloud related diagnostics +diag.add_field('cloud_simple', 'cf', time_avg=True) +diag.add_field('cloud_simple', 'reff_rad', time_avg=True) +diag.add_field('cloud_simple', 'frac_liq', time_avg=True) +diag.add_field('cloud_simple', 'qcl_rad', time_avg=True) +diag.add_field('cloud_simple', 'rh_in_cf', time_avg=True) +#diag.add_field('ls_cloud', 'rhcrit', time_avg=True) + +diag.add_field('cloud_cover', 'tot_cld_amt', time_avg=True) +diag.add_field('cloud_cover', 'high_cld_amt', time_avg=True) +diag.add_field('cloud_cover', 'mid_cld_amt', time_avg=True) +diag.add_field('cloud_cover', 'low_cld_amt', time_avg=True) +#diag.add_field('socrates', 'soc_tot_cloud_cover', time_avg=True) + +# Some intermediate outputs from marine strat clouds diag module +# diag.add_field('strat_cloud', 'eis', time_avg=True) +# diag.add_field('strat_cloud', 'ectei', time_avg=True) +# diag.add_field('strat_cloud', 'lts', time_avg=True) +# diag.add_field('strat_cloud', 'ELF', time_avg=True) +# diag.add_field('strat_cloud', 'zlcl', time_avg=True) +# diag.add_field('strat_cloud', 'z700', time_avg=True) +# diag.add_field('strat_cloud', 'gamma700', time_avg=True) +# diag.add_field('strat_cloud', 'gamma_DL', time_avg=True) +# diag.add_field('strat_cloud', 'theta', time_avg=True) +# diag.add_field('strat_cloud', 'dthdp', time_avg=True) +# diag.add_field('strat_cloud', 'beta1', time_avg=True) +# diag.add_field('strat_cloud', 'beta2', time_avg=True) +# diag.add_field('strat_cloud', 'zinv', time_avg=True) +# diag.add_field('strat_cloud', 'alpha', time_avg=True) +# diag.add_field('strat_cloud', 'DS', time_avg=True) +# diag.add_field('strat_cloud', 'IS', time_avg=True) + +exp.diag_table = diag + +# Empty the run directory ready to run +exp.clear_rundir() + +exp.inputfiles = [os.path.join(GFDL_BASE, 'input/rrtm_input_files/ozone_1990.nc'), + os.path.join(base_dir, 'input/era_land_t42_filtered.nc'), + os.path.join(base_dir, 'input/sst_clim_amip.nc'), + os.path.join(base_dir, 'input/siconc_clim_amip.nc')] + +# Define values for the 'core' namelist +exp.namelist = Namelist({ + 'main_nml':{ + 'days' : 30, + 'hours' : 0, + 'minutes' : 0, + 'seconds' : 0, + 'dt_atmos': 720, # 600 + 'current_date': [1,1,1,0,0,0], + 'calendar': 'thirty_day' + }, + + 'socrates_rad_nml': { + 'stellar_constant': 1370., + #'lw_spectral_filename': os.path.join(GFDL_BASE, 'src/atmos_param/socrates/src/trunk/data/spectra/ga7/sp_lw_ga7'), + #'sw_spectral_filename': os.path.join(GFDL_BASE, 'src/atmos_param/socrates/src/trunk/data/spectra/ga7/sp_sw_ga7'), + 'lw_spectral_filename': os.path.join(GFDL_BASE, 'src/atmos_param/socrates/src/trunk/data/spectra/ga3_1/sp_lw_ga3_1'), + 'sw_spectral_filename': os.path.join(GFDL_BASE, 'src/atmos_param/socrates/src/trunk/data/spectra/ga3_1/sp_sw_ga3_0'), + 'do_read_ozone': True, + 'ozone_file_name' : 'ozone_1990', + 'ozone_field_name': 'ozone_1990', + 'dt_rad': 4320, # 3600, + 'store_intermediate_rad': True, + 'chunk_size': 16, + 'use_pressure_interp_for_half_levels': False, + 'tidally_locked': False, + }, + + 'idealized_moist_phys_nml': { + 'do_damping': True, + 'turb': True, + 'mixed_layer_bc': True, + 'do_virtual': False, + 'do_simple': True, + 'roughness_mom' : 3.21e-05, + 'roughness_heat' : 3.21e-05, + 'roughness_moist': 3.21e-05, + 'two_stream_gray': False, # Use the grey radiation scheme + 'do_socrates_radiation': True, + 'convection_scheme': 'SIMPLE_BETTS_MILLER', # Use simple Betts miller convection + 'do_cloud_simple': True, # Turn on the cloud scheme switch + 'land_option': 'input', + 'land_file_name': 'INPUT/era_land_t42_filtered.nc', + 'land_roughness_prefactor': 10.0, + 'roughness_mom' : 2.e-04, # Ocean roughness lengths + 'roughness_heat' : 2.e-04, # Ocean roughness lengths + 'roughness_moist': 2.e-04, # Ocean roughness lengths + 'bucket': True, # Run with the bucket model + 'init_bucket_depth_land': 0.15, + }, + + # Using linear cloud scheme option + 'cloud_simple_nml': { + 'do_qcl_with_temp': True, + 'do_cloud_cover_diags': True, + 'do_add_stratocumulus': True, + 'reff_liq': 14, # Units: micron + 'reff_ice': 25, # Units: micron + 'qcl_val': 0.18, # Units: g/kg, not kg/kg + }, + + 'large_scale_cloud_nml': { + 'cf_diag_formula_name': 'linear', + 'do_adjust_cld_by_omega': False, + 'do_freezedry': True, + 'qv_polar_val': 0.006, # Units: kg/kg + 'freezedry_power': 2.5, + 'do_fitted_rhcrit': False, + 'linear_a_surf': 42, + 'linear_a_top': 13, + 'linear_power': 11, + }, + + 'marine_strat_cloud_nml': { + 'sc_diag_method': 'Park_ELF', + 'intermediate_outputs_diags': False, + 'dthdp_min_threshold': -0.08, + 'park_a': 1.3, + 'park_b': -0.1, + }, + + 'cloud_cover_diag_nml':{ + 'overlap_assumption': 'maximum-random', # or 'maximum', 'random' + 'mid_cld_bottom': 7.0e4, + 'high_cld_bottom': 4.0e4, + 'cf_min': 0, + }, + + 'vert_turb_driver_nml': { + 'do_mellor_yamada': False, # default: True + 'do_diffusivity': True, # default: False + 'do_simple': True, # default: False + 'constant_gust': 0.0, # default: 1.0 + 'use_tau': False + }, + + 'diffusivity_nml': { + 'do_entrain': True, #False, + 'do_simple': True, + }, + + 'surface_flux_nml': { + 'use_virtual_temp': False, + 'do_simple': True, + 'old_dtaudv': True, + 'land_humidity_prefactor': 1, + 'land_evap_prefactor': 0.6, + #'ocean_evap_prefactor': 1, + }, + + 'atmosphere_nml': { + 'idealized_moist_model': True + }, + + #Use a large mixed-layer depth, and the Albedo of the CTRL case in Jucker & Gerber, 2017 + 'mixed_layer_nml': { + 'tconst': 285., + 'prescribe_initial_dist': True, + 'evaporation': True, + 'depth': 20.0, # Depth of mixed layer used + 'land_option': 'input', # Tell mixed layer to get land mask from input file + 'land_h_capacity_prefactor': 0.1, # What factor to multiply mixed-layer depth by over land. + 'albedo_value': 0.12, # Ocean albedo value + 'land_albedo_prefactor': 1.3, # What factor to multiply ocean albedo by over land + 'do_qflux': False, # Don't use the prescribed analytical formula for q-fluxes + 'do_read_sst': True, # Read in sst values from input file + 'do_sc_sst': True, # Do specified ssts (need both to be true) + 'sst_file': 'sst_clim_amip', # Set name of sst input file + 'specify_sst_over_ocean_only': True, # Make sure sst only specified in regions of ocean. + # Copy from realistic_continents namelist + 'update_albedo_from_ice': True, # Use the simple ice model to update surface albedo + 'ice_albedo_value': 0.7, # What value of albedo to use in regions of ice + #'ice_concentration_threshold': 0.5, # ice concentration threshold above which to make albedo equal to ice_albedo_value + 'ice_albedo_method': 'ramp_function', + }, + + 'qe_moist_convection_nml': { + 'rhbm': 0.7, + 'Tmin': 160., + 'Tmax': 350., + }, + + 'lscale_cond_nml': { + 'do_simple': True, + 'do_evap': True, + }, + + 'sat_vapor_pres_nml': { + 'do_simple': True, + 'construct_table_wrt_liq_and_ice': True, + 'show_all_bad_values': True, + }, + + 'damping_driver_nml': { + 'do_rayleigh': True, + 'trayfric': -0.5, # neg. value: time in *days* + 'sponge_pbottom': 150., # Setting the lower pressure boundary for the model sponge layer in Pa. + 'do_conserve_energy': True, + }, + + # FMS Framework configuration + 'diag_manager_nml': { + 'mix_snapshot_average_fields': False # time avg fields are labelled with time in middle of window + }, + + 'fms_nml': { + 'domains_stack_size': 600000 # default: 0 + }, + + 'fms_io_nml': { + 'threading_write': 'single', # default: multi + 'fileset_write': 'single', # default: multi + }, + + 'spectral_dynamics_nml': { + 'damping_order': 4, + 'water_correction_limit': 200.e2, + 'reference_sea_level_press': 1.0e5, + 'num_levels': NUM_LEVELS, # How many model pressure levels to use + 'valid_range_t': [100., 800.], + 'initial_sphum': [2.e-6], + 'vert_coord_option': 'uneven_sigma', + 'surf_res': 0.03, # 0.2, # Parameter that sets the vertical distribution of sigma levels + 'scale_heights': 11.0, + 'exponent': 7.0, + 'robert_coeff': 0.03, + 'ocean_topog_smoothing': 0.8, + }, + + 'spectral_init_cond_nml':{ + 'topog_file_name': 'era_land_t42_filtered.nc', + 'topography_option': 'input', + }, +}) + + +if __name__=="__main__": + + cb.compile(debug=False) + + OVERWRITE = False + + # Set up the experiment object, with the first argument being the experiment name. + # This will be the name of the folder that the data will appear in. + exp.run(1, use_restart=False, num_cores=NCORES, overwrite_data=OVERWRITE) + for i in range(2, 25): + exp.run(i, num_cores=NCORES, overwrite_data=OVERWRITE) diff --git a/exp/test_cases/socrates_test/socrates_aquaplanet.py b/exp/test_cases/socrates_test/socrates_aquaplanet.py index 2a4bc775f..053c5d276 100644 --- a/exp/test_cases/socrates_test/socrates_aquaplanet.py +++ b/exp/test_cases/socrates_test/socrates_aquaplanet.py @@ -23,7 +23,7 @@ # create an Experiment object to handle the configuration of model parameters # and output diagnostics -exp = Experiment('soc_test_aquaplanet', codebase=cb) +exp = Experiment('soc_aquaplanet', codebase=cb) exp.clear_rundir() inputfiles = [os.path.join(GFDL_BASE,'input/rrtm_input_files/ozone_1990.nc')] @@ -32,23 +32,28 @@ diag = DiagTable() diag.add_file('atmos_monthly', 30, 'days', time_units='days') -#Tell model which diagnostics to write +#Write out diagnostics need for vertical interpolation post-processing diag.add_field('dynamics', 'ps', time_avg=True) diag.add_field('dynamics', 'bk') diag.add_field('dynamics', 'pk') +diag.add_field('dynamics', 'zsurf') + +#Tell model which diagnostics to write diag.add_field('atmosphere', 'precipitation', time_avg=True) +diag.add_field('atmosphere', 'rh', time_avg=True) diag.add_field('mixed_layer', 't_surf', time_avg=True) +diag.add_field('mixed_layer', 'flux_t', time_avg=True) #SH +diag.add_field('mixed_layer', 'flux_lhe', time_avg=True) #LH diag.add_field('dynamics', 'sphum', time_avg=True) diag.add_field('dynamics', 'ucomp', time_avg=True) diag.add_field('dynamics', 'vcomp', time_avg=True) +diag.add_field('dynamics', 'omega', time_avg=True) diag.add_field('dynamics', 'temp', time_avg=True) -diag.add_field('dynamics', 'vor', time_avg=True) -diag.add_field('dynamics', 'div', time_avg=True) -#radiative tendencies -diag.add_field('socrates', 'soc_tdt_lw', time_avg=True) +#temperature tendency - units are K/s +diag.add_field('socrates', 'soc_tdt_lw', time_avg=True) # net flux lw 3d (up - down) diag.add_field('socrates', 'soc_tdt_sw', time_avg=True) -diag.add_field('socrates', 'soc_tdt_rad', time_avg=True) +diag.add_field('socrates', 'soc_tdt_rad', time_avg=True) #sum of the sw and lw heating rates #net (up) and down surface fluxes diag.add_field('socrates', 'soc_surf_flux_lw', time_avg=True) @@ -60,14 +65,6 @@ diag.add_field('socrates', 'soc_toa_sw', time_avg=True) diag.add_field('socrates', 'soc_toa_sw_down', time_avg=True) -# additional output options commented out -#diag.add_field('socrates', 'soc_flux_lw', time_avg=True) -#diag.add_field('socrates', 'soc_flux_sw', time_avg=True) -#diag.add_field('socrates', 'soc_co2', time_avg=True) -#diag.add_field('socrates', 'soc_ozone', time_avg=True) -#diag.add_field('socrates', 'soc_coszen', time_avg=True) -#diag.add_field('socrates', 'soc_spectral_olr', time_avg=True) - exp.diag_table = diag exp.inputfiles = inputfiles @@ -94,7 +91,7 @@ 'chunk_size': 16, 'use_pressure_interp_for_half_levels':False, 'tidally_locked':False, - #'solday': 90 + 'solday':90 }, 'idealized_moist_phys_nml': { 'do_damping': True, @@ -117,7 +114,7 @@ 'constant_gust': 0.0, # default: 1.0 'use_tau': False }, - + 'diffusivity_nml': { 'do_entrain':False, 'do_simple': True, @@ -197,9 +194,11 @@ #Lets do a run! if __name__=="__main__": - cb.compile() + cb.compile(debug=False) #Set up the experiment object, with the first argument being the experiment name. #This will be the name of the folder that the data will appear in. + exp.run(1, use_restart=False, num_cores=NCORES, overwrite_data=False) + for i in range(2,121): - exp.run(i, num_cores=NCORES) + exp.run(i, num_cores=NCORES, overwrite_data=overwrite) diff --git a/exp/test_cases/socrates_test/socrates_aquaplanet_amip_with_original_era_topo_testing_only.py b/exp/test_cases/socrates_test/socrates_aquaplanet_amip_with_original_era_topo_testing_only.py new file mode 100644 index 000000000..f26730041 --- /dev/null +++ b/exp/test_cases/socrates_test/socrates_aquaplanet_amip_with_original_era_topo_testing_only.py @@ -0,0 +1,235 @@ +import os + +import numpy as np + +from isca import SocratesCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE +from isca.util import exp_progress + +NCORES = 16 +base_dir = os.path.dirname(os.path.realpath(__file__)) +# a CodeBase can be a directory on the computer, +# useful for iterative development +cb = SocratesCodeBase.from_directory(GFDL_BASE) + +# or it can point to a specific git repo and commit id. +# This method should ensure future, independent, reproducibility of results. +# cb = DryCodeBase.from_repo(repo='https://github.com/isca/isca', commit='isca1.1') + +# compilation depends on computer specific settings. The $GFDL_ENV +# environment variable is used to determine which `$GFDL_BASE/src/extra/env` file +# is used to load the correct compilers. The env file is always loaded from +# $GFDL_BASE and not the checked out git repo. + +# create an Experiment object to handle the configuration of model parameters +# and output diagnostics + +# note on this experiement. +# The original era land file will generate Gibbs ripples if land is used. +# This files only purpose if for testing the behaviour of era land. +# +exp = Experiment('soc_aquaplanet_amip_old_era_land', codebase=cb) +exp.clear_rundir() + +inputfiles = [os.path.join(GFDL_BASE,'input/rrtm_input_files/ozone_1990.nc'), + os.path.join(GFDL_BASE,'input/land_masks/era_land_t42.nc'), + os.path.join(GFDL_BASE,'exp/test_cases/realistic_continents/input/sst_clim_amip.nc'), + os.path.join(GFDL_BASE,'exp/test_cases/realistic_continents/input/siconc_clim_amip.nc'), + ] + +#Tell model how to write diagnostics +diag = DiagTable() +diag.add_file('atmos_monthly', 30, 'days', time_units='days') + +#Write out diagnostics need for vertical interpolation post-processing +diag.add_field('dynamics', 'ps', time_avg=True) +diag.add_field('dynamics', 'bk') +diag.add_field('dynamics', 'pk') +diag.add_field('dynamics', 'zsurf') + +#Tell model which diagnostics to write +diag.add_field('atmosphere', 'precipitation', time_avg=True) +diag.add_field('atmosphere', 'rh', time_avg=True) +diag.add_field('mixed_layer', 't_surf', time_avg=True) +diag.add_field('mixed_layer', 'flux_t', time_avg=True) #SH +diag.add_field('mixed_layer', 'flux_lhe', time_avg=True) #LH +diag.add_field('dynamics', 'sphum', time_avg=True) +diag.add_field('dynamics', 'ucomp', time_avg=True) +diag.add_field('dynamics', 'vcomp', time_avg=True) +diag.add_field('dynamics', 'omega', time_avg=True) +diag.add_field('dynamics', 'temp', time_avg=True) + +#temperature tendency - units are K/s +diag.add_field('socrates', 'soc_tdt_lw', time_avg=True) # net flux lw 3d (up - down) +diag.add_field('socrates', 'soc_tdt_sw', time_avg=True) +diag.add_field('socrates', 'soc_tdt_rad', time_avg=True) #sum of the sw and lw heating rates + +#net (up) and down surface fluxes +diag.add_field('socrates', 'soc_surf_flux_lw', time_avg=True) +diag.add_field('socrates', 'soc_surf_flux_sw', time_avg=True) +diag.add_field('socrates', 'soc_surf_flux_lw_down', time_avg=True) +diag.add_field('socrates', 'soc_surf_flux_sw_down', time_avg=True) +#net (up) TOA and downard fluxes +diag.add_field('socrates', 'soc_olr', time_avg=True) +diag.add_field('socrates', 'soc_toa_sw', time_avg=True) +diag.add_field('socrates', 'soc_toa_sw_down', time_avg=True) + +exp.diag_table = diag +exp.inputfiles = inputfiles + +#Define values for the 'core' namelist +exp.namelist = namelist = Namelist({ + 'main_nml':{ + 'days' : 30, + 'hours' : 0, + 'minutes': 0, + 'seconds': 0, + 'dt_atmos':600, + 'current_date' : [1,1,1,0,0,0], + 'calendar' : 'thirty_day' + }, + 'socrates_rad_nml': { + 'stellar_constant':1370., + 'lw_spectral_filename':os.path.join(GFDL_BASE,'src/atmos_param/socrates/src/trunk/data/spectra/ga7/sp_lw_ga7'), + 'sw_spectral_filename':os.path.join(GFDL_BASE,'src/atmos_param/socrates/src/trunk/data/spectra/ga7/sp_sw_ga7'), + 'do_read_ozone': True, + 'ozone_file_name':'ozone_1990', + 'ozone_field_name':'ozone_1990', + 'dt_rad':3600, + 'store_intermediate_rad':True, + 'chunk_size': 16, + 'use_pressure_interp_for_half_levels':False, + 'tidally_locked':False, + 'solday':90 + }, + 'idealized_moist_phys_nml': { + 'do_damping': True, + 'turb':True, + 'mixed_layer_bc':True, + 'do_virtual' :False, + 'do_simple': True, + 'roughness_mom':3.21e-05, + 'roughness_heat':3.21e-05, + 'roughness_moist':3.21e-05, + 'two_stream_gray': False, #Use the grey radiation scheme + 'do_socrates_radiation': True, + 'convection_scheme': 'SIMPLE_BETTS_MILLER', #Use simple Betts miller convection + 'do_cloud_simple': False, + 'land_option' : 'input', + 'land_file_name' : 'INPUT/era_land_t42.nc', + 'land_roughness_prefactor' :10.0, + }, + + + 'vert_turb_driver_nml': { + 'do_mellor_yamada': False, # default: True + 'do_diffusivity': True, # default: False + 'do_simple': True, # default: False + 'constant_gust': 0.0, # default: 1.0 + 'use_tau': False + }, + + 'diffusivity_nml': { + 'do_entrain':False, + 'do_simple': True, + }, + + 'surface_flux_nml': { + 'use_virtual_temp': False, + 'do_simple': True, + 'old_dtaudv': True, + 'land_humidity_prefactor': 0.7, + }, + + 'atmosphere_nml': { + 'idealized_moist_model': True + }, + + #Use a large mixed-layer depth, and the Albedo of the CTRL case in Jucker & Gerber, 2017 + 'mixed_layer_nml': { + 'tconst' : 285., + 'prescribe_initial_dist':True, + 'evaporation':True, + 'depth': 20.0, #Depth of mixed layer used + 'albedo_value': 0.2, #Albedo value used + 'land_option': 'input', #Tell mixed layer to get land mask from input file + 'land_h_capacity_prefactor': 0.1, #What factor to multiply mixed-layer depth by over land. + 'albedo_value': 0.25, #Ocean albedo value + 'land_albedo_prefactor': 1.3, #What factor to multiply ocean albedo by over land + 'do_qflux' : False, #Don't use the prescribed analytical formula for q-fluxes + 'do_read_sst' : True, #Read in sst values from input file + 'do_sc_sst' : True, #Do specified ssts (need both to be true) + 'sst_file' : 'sst_clim_amip', #Set name of sst input file + 'specify_sst_over_ocean_only' : True, #Make sure sst only specified in regions of ocean. + }, + + 'qe_moist_convection_nml': { + 'rhbm':0.7, + 'Tmin':160., + 'Tmax':350. + }, + + 'lscale_cond_nml': { + 'do_simple':True, + 'do_evap':True + }, + + 'sat_vapor_pres_nml': { + 'do_simple':True, + 'construct_table_wrt_liq_and_ice':True + }, + + 'damping_driver_nml': { + 'do_rayleigh': True, + 'trayfric': -0.5, # neg. value: time in *days* + 'sponge_pbottom': 150., #Setting the lower pressure boundary for the model sponge layer in Pa. + 'do_conserve_energy': True, + }, + + # FMS Framework configuration + 'diag_manager_nml': { + 'mix_snapshot_average_fields': False # time avg fields are labelled with time in middle of window + }, + + 'fms_nml': { + 'domains_stack_size': 600000 # default: 0 + }, + + 'fms_io_nml': { + 'threading_write': 'single', # default: multi + 'fileset_write': 'single', # default: multi + }, + + 'spectral_dynamics_nml': { + 'damping_order': 4, + 'water_correction_limit': 200.e2, + 'reference_sea_level_press':1.0e5, + 'num_levels':40, #How many model pressure levels to use + 'valid_range_t':[100.,800.], + 'initial_sphum':[2.e-6], + 'vert_coord_option':'uneven_sigma', + 'surf_res':0.2, #Parameter that sets the vertical distribution of sigma levels + 'scale_heights' : 11.0, + 'exponent':7.0, + 'robert_coeff':0.03, + 'ocean_topog_smoothing': 0.8 + }, + + 'spectral_init_cond_nml':{ + 'topog_file_name': 'era_land_t42.nc', + 'topography_option': 'input' + }, + +}) + +#Lets do a run! +if __name__=="__main__": + + cb.compile(debug=False) + #Set up the experiment object, with the first argument being the experiment name. + #This will be the name of the folder that the data will appear in. + + overwrite=False + + exp.run(1, use_restart=False, num_cores=NCORES, overwrite_data=overwrite)#, run_idb=True) + for i in range(2,61): + exp.run(i, num_cores=NCORES, overwrite_data=overwrite) diff --git a/exp/test_cases/socrates_test/socrates_aquaplanet_amip_with_topo.py b/exp/test_cases/socrates_test/socrates_aquaplanet_amip_with_topo.py new file mode 100644 index 000000000..a8cf5a3e6 --- /dev/null +++ b/exp/test_cases/socrates_test/socrates_aquaplanet_amip_with_topo.py @@ -0,0 +1,227 @@ +import os + +import numpy as np + +from isca import SocratesCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE +from isca.util import exp_progress + +NCORES = 16 +base_dir = os.path.dirname(os.path.realpath(__file__)) +# a CodeBase can be a directory on the computer, +# useful for iterative development +cb = SocratesCodeBase.from_directory(GFDL_BASE) + +# or it can point to a specific git repo and commit id. +# This method should ensure future, independent, reproducibility of results. +# cb = DryCodeBase.from_repo(repo='https://github.com/isca/isca', commit='isca1.1') + +# compilation depends on computer specific settings. The $GFDL_ENV +# environment variable is used to determine which `$GFDL_BASE/src/extra/env` file +# is used to load the correct compilers. The env file is always loaded from +# $GFDL_BASE and not the checked out git repo. + +# create an Experiment object to handle the configuration of model parameters +# and output diagnostics + +exp = Experiment('soc_aquaplanet_amip', codebase=cb) +exp.clear_rundir() + +exp.inputfiles = [os.path.join(GFDL_BASE,'input/rrtm_input_files/ozone_1990.nc'), + os.path.join(GFDL_BASE,'exp/test_cases/realistic_continents/input/era-spectral7_T42_64x128.out.nc'), + os.path.join(GFDL_BASE,'exp/test_cases/realistic_continents/input/sst_clim_amip.nc'), + os.path.join(GFDL_BASE,'exp/test_cases/realistic_continents/input/siconc_clim_amip.nc')] + +#Tell model how to write diagnostics +diag = DiagTable() +diag.add_file('atmos_monthly', 30, 'days', time_units='days') + +#Write out diagnostics need for vertical interpolation post-processing +diag.add_field('dynamics', 'ps', time_avg=True) +diag.add_field('dynamics', 'bk') +diag.add_field('dynamics', 'pk') +diag.add_field('dynamics', 'zsurf') + +#Tell model which diagnostics to write +diag.add_field('atmosphere', 'precipitation', time_avg=True) +diag.add_field('atmosphere', 'rh', time_avg=True) +diag.add_field('mixed_layer', 't_surf', time_avg=True) +diag.add_field('mixed_layer', 'flux_t', time_avg=True) #SH +diag.add_field('mixed_layer', 'flux_lhe', time_avg=True) #LH +diag.add_field('dynamics', 'sphum', time_avg=True) +diag.add_field('dynamics', 'ucomp', time_avg=True) +diag.add_field('dynamics', 'vcomp', time_avg=True) +diag.add_field('dynamics', 'omega', time_avg=True) +diag.add_field('dynamics', 'temp', time_avg=True) + +#temperature tendency - units are K/s +diag.add_field('socrates', 'soc_tdt_lw', time_avg=True) # net flux lw 3d (up - down) +diag.add_field('socrates', 'soc_tdt_sw', time_avg=True) +diag.add_field('socrates', 'soc_tdt_rad', time_avg=True) #sum of the sw and lw heating rates + +#net (up) and down surface fluxes +diag.add_field('socrates', 'soc_surf_flux_lw', time_avg=True) +diag.add_field('socrates', 'soc_surf_flux_sw', time_avg=True) +diag.add_field('socrates', 'soc_surf_flux_lw_down', time_avg=True) +diag.add_field('socrates', 'soc_surf_flux_sw_down', time_avg=True) +#net (up) TOA and downard fluxes +diag.add_field('socrates', 'soc_olr', time_avg=True) +diag.add_field('socrates', 'soc_toa_sw', time_avg=True) +diag.add_field('socrates', 'soc_toa_sw_down', time_avg=True) + +exp.diag_table = diag + +#Define values for the 'core' namelist +exp.namelist = namelist = Namelist({ + 'main_nml':{ + 'days' : 30, + 'hours' : 0, + 'minutes': 0, + 'seconds': 0, + 'dt_atmos':600, + 'current_date' : [1,1,1,0,0,0], + 'calendar' : 'thirty_day' + }, + 'socrates_rad_nml': { + 'stellar_constant':1370., + 'lw_spectral_filename':os.path.join(GFDL_BASE,'src/atmos_param/socrates/src/trunk/data/spectra/ga7/sp_lw_ga7'), + 'sw_spectral_filename':os.path.join(GFDL_BASE,'src/atmos_param/socrates/src/trunk/data/spectra/ga7/sp_sw_ga7'), + 'do_read_ozone': True, + 'ozone_file_name':'ozone_1990', + 'ozone_field_name':'ozone_1990', + 'dt_rad':3600, + 'store_intermediate_rad':True, + 'chunk_size': 16, + 'use_pressure_interp_for_half_levels':False, + 'tidally_locked':False, + 'solday':90 + }, + 'idealized_moist_phys_nml': { + 'do_damping': True, + 'turb':True, + 'mixed_layer_bc':True, + 'do_virtual' :False, + 'do_simple': True, + 'roughness_mom':3.21e-05, + 'roughness_heat':3.21e-05, + 'roughness_moist':3.21e-05, + 'two_stream_gray': False, #Use the grey radiation scheme + 'do_socrates_radiation': True, + 'convection_scheme': 'SIMPLE_BETTS_MILLER', #Use simple Betts miller convection + 'do_cloud_simple': False, + 'land_option' : 'input', + 'land_file_name' : 'INPUT/era-spectral7_T42_64x128.out.nc', + 'land_roughness_prefactor' :10.0, + }, + + + 'vert_turb_driver_nml': { + 'do_mellor_yamada': False, # default: True + 'do_diffusivity': True, # default: False + 'do_simple': True, # default: False + 'constant_gust': 0.0, # default: 1.0 + 'use_tau': False + }, + + 'diffusivity_nml': { + 'do_entrain':False, + 'do_simple': True, + }, + + 'surface_flux_nml': { + 'use_virtual_temp': False, + 'do_simple': True, + 'old_dtaudv': True, + 'land_humidity_prefactor': 0.7, + }, + + 'atmosphere_nml': { + 'idealized_moist_model': True + }, + + #Use a large mixed-layer depth, and the Albedo of the CTRL case in Jucker & Gerber, 2017 + 'mixed_layer_nml': { + 'tconst' : 285., + 'prescribe_initial_dist':True, + 'evaporation':True, + 'land_option': 'input', #Tell mixed layer to get land mask from input file + 'land_h_capacity_prefactor': 0.1, #What factor to multiply mixed-layer depth by over land. + 'albedo_value': 0.25, #albedo value + 'land_albedo_prefactor': 1.3, #What factor to multiply ocean albedo by over land + 'do_qflux' : False, #Don't use the prescribed analytical formula for q-fluxes + 'do_read_sst' : True, #Read in sst values from input file + 'do_sc_sst' : True, #Do specified ssts (need both to be true) + 'sst_file' : 'sst_clim_amip', #Set name of sst input file + 'specify_sst_over_ocean_only' : True, #Make sure sst only specified in regions of ocean. + }, + + 'qe_moist_convection_nml': { + 'rhbm':0.7, + 'Tmin':160., + 'Tmax':350. + }, + + 'lscale_cond_nml': { + 'do_simple':True, + 'do_evap':True + }, + + 'sat_vapor_pres_nml': { + 'do_simple':True, + 'construct_table_wrt_liq_and_ice':True + }, + + 'damping_driver_nml': { + 'do_rayleigh': True, + 'trayfric': -0.5, # neg. value: time in *days* + 'sponge_pbottom': 150., #Setting the lower pressure boundary for the model sponge layer in Pa. + 'do_conserve_energy': True, + }, + + # FMS Framework configuration + 'diag_manager_nml': { + 'mix_snapshot_average_fields': False # time avg fields are labelled with time in middle of window + }, + + 'fms_nml': { + 'domains_stack_size': 600000 # default: 0 + }, + + 'fms_io_nml': { + 'threading_write': 'single', # default: multi + 'fileset_write': 'single', # default: multi + }, + + 'spectral_dynamics_nml': { + 'damping_order': 4, + 'water_correction_limit': 200.e2, + 'reference_sea_level_press':1.0e5, + 'num_levels':40, #How many model pressure levels to use + 'valid_range_t':[100.,800.], + 'initial_sphum':[2.e-6], + 'vert_coord_option':'uneven_sigma', + 'surf_res':0.2, #Parameter that sets the vertical distribution of sigma levels + 'scale_heights' : 11.0, + 'exponent':7.0, + 'robert_coeff':0.03, + 'ocean_topog_smoothing': 0.0 + }, + + 'spectral_init_cond_nml':{ + 'topog_file_name': 'era-spectral7_T42_64x128.out.nc', + 'topography_option': 'input' + }, + +}) + +#Lets do a run! +if __name__=="__main__": + + cb.compile(debug=False) + #Set up the experiment object, with the first argument being the experiment name. + #This will be the name of the folder that the data will appear in. + + overwrite=False + + exp.run(1, use_restart=False, num_cores=NCORES, overwrite_data=overwrite)#, run_idb=True) + for i in range(2,61): + exp.run(i, num_cores=NCORES, overwrite_data=overwrite) diff --git a/exp/test_cases/socrates_test/socrates_aquaplanet_amip_with_topo_clouds.py b/exp/test_cases/socrates_test/socrates_aquaplanet_amip_with_topo_clouds.py new file mode 100644 index 000000000..9bda51c48 --- /dev/null +++ b/exp/test_cases/socrates_test/socrates_aquaplanet_amip_with_topo_clouds.py @@ -0,0 +1,248 @@ +import os + +import numpy as np + +from isca import SocratesCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE +from isca.util import exp_progress + +NCORES = 16 +base_dir = os.path.dirname(os.path.realpath(__file__)) +# a CodeBase can be a directory on the computer, +# useful for iterative development +cb = SocratesCodeBase.from_directory(GFDL_BASE) + +# or it can point to a specific git repo and commit id. +# This method should ensure future, independent, reproducibility of results. +# cb = DryCodeBase.from_repo(repo='https://github.com/isca/isca', commit='isca1.1') + +# compilation depends on computer specific settings. The $GFDL_ENV +# environment variable is used to determine which `$GFDL_BASE/src/extra/env` file +# is used to load the correct compilers. The env file is always loaded from +# $GFDL_BASE and not the checked out git repo. + +# create an Experiment object to handle the configuration of model parameters +# and output diagnostics + +exp = Experiment('soc_aquaplanet_amip_clouds', codebase=cb) +exp.clear_rundir() + +exp.inputfiles = [os.path.join(GFDL_BASE,'input/rrtm_input_files/ozone_1990.nc'), + os.path.join(GFDL_BASE,'exp/test_cases/realistic_continents/input/era-spectral7_T42_64x128.out.nc'), + os.path.join(GFDL_BASE,'exp/test_cases/realistic_continents/input/sst_clim_amip.nc'), + os.path.join(GFDL_BASE,'exp/test_cases/realistic_continents/input/siconc_clim_amip.nc')] + +#Tell model how to write diagnostics +diag = DiagTable() +diag.add_file('atmos_monthly', 30, 'days', time_units='days') + +#Write out diagnostics need for vertical interpolation post-processing +diag.add_field('dynamics', 'ps', time_avg=True) +diag.add_field('dynamics', 'bk') +diag.add_field('dynamics', 'pk') +diag.add_field('dynamics', 'zsurf') + +#Tell model which diagnostics to write +diag.add_field('atmosphere', 'precipitation', time_avg=True) +diag.add_field('atmosphere', 'rh', time_avg=True) +diag.add_field('mixed_layer', 't_surf', time_avg=True) +diag.add_field('mixed_layer', 'flux_t', time_avg=True) #SH +diag.add_field('mixed_layer', 'flux_lhe', time_avg=True) #LH +diag.add_field('dynamics', 'sphum', time_avg=True) +diag.add_field('dynamics', 'ucomp', time_avg=True) +diag.add_field('dynamics', 'vcomp', time_avg=True) +diag.add_field('dynamics', 'omega', time_avg=True) +diag.add_field('dynamics', 'temp', time_avg=True) + +#temperature tendency - units are K/s +diag.add_field('socrates', 'soc_tdt_lw', time_avg=True) # net flux lw 3d (up - down) +diag.add_field('socrates', 'soc_tdt_sw', time_avg=True) +diag.add_field('socrates', 'soc_tdt_rad', time_avg=True) #sum of the sw and lw heating rates + +#net (up) and down surface fluxes +diag.add_field('socrates', 'soc_surf_flux_lw', time_avg=True) +diag.add_field('socrates', 'soc_surf_flux_sw', time_avg=True) +diag.add_field('socrates', 'soc_surf_flux_lw_down', time_avg=True) +diag.add_field('socrates', 'soc_surf_flux_sw_down', time_avg=True) +#net (up) TOA and downard fluxes +diag.add_field('socrates', 'soc_olr', time_avg=True) +diag.add_field('socrates', 'soc_toa_sw', time_avg=True) +diag.add_field('socrates', 'soc_toa_sw_down', time_avg=True) + +#clear sky fluxes +diag.add_field('socrates', 'soc_surf_flux_lw_clear', time_avg=True) +diag.add_field('socrates', 'soc_surf_flux_sw_clear', time_avg=True) +diag.add_field('socrates', 'soc_surf_flux_lw_down_clear', time_avg=True) +diag.add_field('socrates', 'soc_surf_flux_sw_down_clear', time_avg=True) +diag.add_field('socrates', 'soc_olr_clear', time_avg=True) +diag.add_field('socrates', 'soc_toa_sw_clear', time_avg=True) +diag.add_field('socrates', 'soc_toa_sw_down_clear', time_avg=True) + +diag.add_field('cloud_simple', 'cf', time_avg=True) +diag.add_field('cloud_simple', 'reff_rad', time_avg=True) +diag.add_field('cloud_simple', 'frac_liq', time_avg=True) +diag.add_field('cloud_simple', 'qcl_rad', time_avg=True) +#diag.add_field('cloud_simple', 'simple_rhcrit', time_avg=True) +diag.add_field('cloud_simple', 'rh_min', time_avg=True) +diag.add_field('cloud_simple', 'rh_in_cf', time_avg=True) +diag.add_field('mixed_layer', 'albedo', time_avg=True) + +exp.diag_table = diag + +#Define values for the 'core' namelist +exp.namelist = namelist = Namelist({ + 'main_nml':{ + 'days' : 30, + 'hours' : 0, + 'minutes': 0, + 'seconds': 0, + 'dt_atmos':600, + 'current_date' : [1,1,1,0,0,0], + 'calendar' : 'thirty_day' + }, + 'socrates_rad_nml': { + 'stellar_constant':1370., + 'lw_spectral_filename':os.path.join(GFDL_BASE,'src/atmos_param/socrates/src/trunk/data/spectra/ga7/sp_lw_ga7'), + 'sw_spectral_filename':os.path.join(GFDL_BASE,'src/atmos_param/socrates/src/trunk/data/spectra/ga7/sp_sw_ga7'), + 'do_read_ozone': True, + 'ozone_file_name':'ozone_1990', + 'ozone_field_name':'ozone_1990', + 'dt_rad':3600, + 'store_intermediate_rad':True, + 'chunk_size': 16, + 'use_pressure_interp_for_half_levels':False, + 'tidally_locked':False, + 'solday':90 + }, + 'idealized_moist_phys_nml': { + 'do_damping': True, + 'turb':True, + 'mixed_layer_bc':True, + 'do_virtual' :False, + 'do_simple': True, + 'roughness_mom':3.21e-05, + 'roughness_heat':3.21e-05, + 'roughness_moist':3.21e-05, + 'two_stream_gray': False, #Use the grey radiation scheme + 'do_socrates_radiation': True, + 'convection_scheme': 'SIMPLE_BETTS_MILLER', #Use simple Betts miller convection + 'do_cloud_spookie': True, # this is where the clouds scheme is turned on + 'land_option' : 'input', + 'land_file_name' : 'INPUT/era-spectral7_T42_64x128.out.nc', + 'land_roughness_prefactor' :10.0, + }, + + 'cloud_spookie_nml': { #use all existing defaults as in code + 'spookie_protocol':2 + }, + + 'vert_turb_driver_nml': { + 'do_mellor_yamada': False, # default: True + 'do_diffusivity': True, # default: False + 'do_simple': True, # default: False + 'constant_gust': 0.0, # default: 1.0 + 'use_tau': False + }, + + 'diffusivity_nml': { + 'do_entrain':False, + 'do_simple': True, + }, + + 'surface_flux_nml': { + 'use_virtual_temp': False, + 'do_simple': True, + 'old_dtaudv': True, + 'land_humidity_prefactor': 0.7, + }, + + 'atmosphere_nml': { + 'idealized_moist_model': True + }, + + #Use a large mixed-layer depth, and the Albedo of the CTRL case in Jucker & Gerber, 2017 + 'mixed_layer_nml': { + 'tconst' : 285., + 'prescribe_initial_dist':True, + 'evaporation':True, + 'land_option': 'input', #Tell mixed layer to get land mask from input file + 'land_h_capacity_prefactor': 0.1, #What factor to multiply mixed-layer depth by over land. + 'albedo_value': 0.25, #albedo value + 'land_albedo_prefactor': 1.3, #What factor to multiply ocean albedo by over land + 'do_qflux' : False, #Don't use the prescribed analytical formula for q-fluxes + 'do_read_sst' : True, #Read in sst values from input file + 'do_sc_sst' : True, #Do specified ssts (need both to be true) + 'sst_file' : 'sst_clim_amip', #Set name of sst input file + 'specify_sst_over_ocean_only' : True, #Make sure sst only specified in regions of ocean. + }, + + 'qe_moist_convection_nml': { + 'rhbm':0.7, + 'Tmin':160., + 'Tmax':350. + }, + + 'lscale_cond_nml': { + 'do_simple':True, + 'do_evap':True + }, + + 'sat_vapor_pres_nml': { + 'do_simple':True, + 'construct_table_wrt_liq_and_ice':True + }, + + 'damping_driver_nml': { + 'do_rayleigh': True, + 'trayfric': -0.5, # neg. value: time in *days* + 'sponge_pbottom': 150., #Setting the lower pressure boundary for the model sponge layer in Pa. + 'do_conserve_energy': True, + }, + + # FMS Framework configuration + 'diag_manager_nml': { + 'mix_snapshot_average_fields': False # time avg fields are labelled with time in middle of window + }, + + 'fms_nml': { + 'domains_stack_size': 600000 # default: 0 + }, + + 'fms_io_nml': { + 'threading_write': 'single', # default: multi + 'fileset_write': 'single', # default: multi + }, + + 'spectral_dynamics_nml': { + 'damping_order': 4, + 'water_correction_limit': 200.e2, + 'reference_sea_level_press':1.0e5, + 'num_levels':40, #How many model pressure levels to use + 'valid_range_t':[100.,800.], + 'initial_sphum':[2.e-6], + 'vert_coord_option':'uneven_sigma', + 'surf_res':0.2, #Parameter that sets the vertical distribution of sigma levels + 'scale_heights' : 11.0, + 'exponent':7.0, + 'robert_coeff':0.03, + 'ocean_topog_smoothing': 0.0 + }, + + 'spectral_init_cond_nml':{ + 'topog_file_name': 'era-spectral7_T42_64x128.out.nc', + 'topography_option': 'input' + }, + +}) + +#Lets do a run! +if __name__=="__main__": + + cb.compile(debug=False) + #Set up the experiment object, with the first argument being the experiment name. + #This will be the name of the folder that the data will appear in. + + overwrite=False + + exp.run(1, use_restart=False, num_cores=NCORES, overwrite_data=overwrite)#, run_idb=True) + for i in range(2,61): + exp.run(i, num_cores=NCORES, overwrite_data=overwrite) diff --git a/exp/test_cases/socrates_test/socrates_aquaplanet_cloud.py b/exp/test_cases/socrates_test/socrates_aquaplanet_cloud.py new file mode 100644 index 000000000..b8e6f8beb --- /dev/null +++ b/exp/test_cases/socrates_test/socrates_aquaplanet_cloud.py @@ -0,0 +1,231 @@ +import os + +import numpy as np + +from isca import SocratesCodeBase, DiagTable, Experiment, Namelist, GFDL_BASE +from isca.util import exp_progress + +NCORES = 16 +base_dir = os.path.dirname(os.path.realpath(__file__)) +# a CodeBase can be a directory on the computer, +# useful for iterative development +cb = SocratesCodeBase.from_directory(GFDL_BASE) + +# or it can point to a specific git repo and commit id. +# This method should ensure future, independent, reproducibility of results. +# cb = DryCodeBase.from_repo(repo='https://github.com/isca/isca', commit='isca1.1') + +# compilation depends on computer specific settings. The $GFDL_ENV +# environment variable is used to determine which `$GFDL_BASE/src/extra/env` file +# is used to load the correct compilers. The env file is always loaded from +# $GFDL_BASE and not the checked out git repo. + +# create an Experiment object to handle the configuration of model parameters +# and output diagnostics + +exp = Experiment('soc_aquaplanet_with_clouds', codebase=cb) +exp.clear_rundir() + +inputfiles = [os.path.join(GFDL_BASE,'input/rrtm_input_files/ozone_1990.nc')] + +#Tell model how to write diagnostics +diag = DiagTable() +diag.add_file('atmos_monthly', 30, 'days', time_units='days') + +#Write out diagnostics need for vertical interpolation post-processing +diag.add_field('dynamics', 'ps', time_avg=True) +diag.add_field('dynamics', 'bk') +diag.add_field('dynamics', 'pk') +diag.add_field('dynamics', 'zsurf') + +#Tell model which diagnostics to write +diag.add_field('atmosphere', 'precipitation', time_avg=True) +diag.add_field('atmosphere', 'rh', time_avg=True) +diag.add_field('mixed_layer', 't_surf', time_avg=True) +diag.add_field('mixed_layer', 'flux_t', time_avg=True) #SH +diag.add_field('mixed_layer', 'flux_lhe', time_avg=True) #LH +diag.add_field('dynamics', 'sphum', time_avg=True) +diag.add_field('dynamics', 'ucomp', time_avg=True) +diag.add_field('dynamics', 'vcomp', time_avg=True) +diag.add_field('dynamics', 'omega', time_avg=True) +diag.add_field('dynamics', 'temp', time_avg=True) + +#temperature tendency - units are K/s +diag.add_field('socrates', 'soc_tdt_lw', time_avg=True) # net flux lw 3d (up - down) +diag.add_field('socrates', 'soc_tdt_sw', time_avg=True) +diag.add_field('socrates', 'soc_tdt_rad', time_avg=True) #sum of the sw and lw heating rates + +#net (up) and down surface fluxes +diag.add_field('socrates', 'soc_surf_flux_lw', time_avg=True) +diag.add_field('socrates', 'soc_surf_flux_sw', time_avg=True) +diag.add_field('socrates', 'soc_surf_flux_lw_down', time_avg=True) +diag.add_field('socrates', 'soc_surf_flux_sw_down', time_avg=True) +#net (up) TOA and downard fluxes +diag.add_field('socrates', 'soc_olr', time_avg=True) +diag.add_field('socrates', 'soc_toa_sw', time_avg=True) +diag.add_field('socrates', 'soc_toa_sw_down', time_avg=True) + +#clear sky fluxes +diag.add_field('socrates', 'soc_surf_flux_lw_clear', time_avg=True) +diag.add_field('socrates', 'soc_surf_flux_sw_clear', time_avg=True) +diag.add_field('socrates', 'soc_surf_flux_lw_down_clear', time_avg=True) +diag.add_field('socrates', 'soc_surf_flux_sw_down_clear', time_avg=True) +diag.add_field('socrates', 'soc_olr_clear', time_avg=True) +diag.add_field('socrates', 'soc_toa_sw_clear', time_avg=True) +diag.add_field('socrates', 'soc_toa_sw_down_clear', time_avg=True) + +diag.add_field('cloud_simple', 'cf', time_avg=True) +diag.add_field('cloud_simple', 'reff_rad', time_avg=True) +diag.add_field('cloud_simple', 'frac_liq', time_avg=True) +diag.add_field('cloud_simple', 'qcl_rad', time_avg=True) +#diag.add_field('cloud_simple', 'simple_rhcrit', time_avg=True) +diag.add_field('cloud_simple', 'rh_min', time_avg=True) +diag.add_field('cloud_simple', 'rh_in_cf', time_avg=True) +diag.add_field('mixed_layer', 'albedo') + + +exp.diag_table = diag +exp.inputfiles = inputfiles + +#Define values for the 'core' namelist +exp.namelist = namelist = Namelist({ + 'main_nml':{ + 'days' : 30, + 'hours' : 0, + 'minutes': 0, + 'seconds': 0, + 'dt_atmos':600, + 'current_date' : [1,1,1,0,0,0], + 'calendar' : 'thirty_day' + }, + 'socrates_rad_nml': { + 'stellar_constant':1370., + 'lw_spectral_filename':os.path.join(GFDL_BASE,'src/atmos_param/socrates/src/trunk/data/spectra/ga7/sp_lw_ga7'), + 'sw_spectral_filename':os.path.join(GFDL_BASE,'src/atmos_param/socrates/src/trunk/data/spectra/ga7/sp_sw_ga7'), + 'do_read_ozone': True, + 'ozone_file_name':'ozone_1990', + 'ozone_field_name':'ozone_1990', + 'dt_rad':3600, + 'store_intermediate_rad':True, + 'chunk_size': 16, + 'use_pressure_interp_for_half_levels':False, + 'tidally_locked':False, + 'solday':90 + }, + 'idealized_moist_phys_nml': { + 'do_damping': True, + 'turb':True, + 'mixed_layer_bc':True, + 'do_virtual' :False, + 'do_simple': True, + 'roughness_mom':3.21e-05, + 'roughness_heat':3.21e-05, + 'roughness_moist':3.21e-05, + 'two_stream_gray': False, #Use the grey radiation scheme + 'do_socrates_radiation': True, + 'convection_scheme': 'SIMPLE_BETTS_MILLER', #Use simple Betts miller convection + 'do_cloud_spookie': True # this is where the clouds scheme is turned on + }, + + 'cloud_spookie_nml': { #use all existing defaults as in code + 'spookie_protocol':2 + }, + + 'vert_turb_driver_nml': { + 'do_mellor_yamada': False, # default: True + 'do_diffusivity': True, # default: False + 'do_simple': True, # default: False + 'constant_gust': 0.0, # default: 1.0 + 'use_tau': False + }, + + 'diffusivity_nml': { + 'do_entrain':False, + 'do_simple': True, + }, + + 'surface_flux_nml': { + 'use_virtual_temp': False, + 'do_simple': True, + 'old_dtaudv': True + }, + + 'atmosphere_nml': { + 'idealized_moist_model': True + }, + + #Use a large mixed-layer depth, and the Albedo of the CTRL case in Jucker & Gerber, 2017 + 'mixed_layer_nml': { + 'tconst' : 285., + 'prescribe_initial_dist':True, + 'evaporation':True, + 'depth': 2.5, #Depth of mixed layer used + 'albedo_value': 0.2, #Albedo value used + }, + + 'qe_moist_convection_nml': { + 'rhbm':0.7, + 'Tmin':160., + 'Tmax':350. + }, + + 'lscale_cond_nml': { + 'do_simple':True, + 'do_evap':True + }, + + 'sat_vapor_pres_nml': { + 'do_simple':True, + 'construct_table_wrt_liq_and_ice':True + }, + + 'damping_driver_nml': { + 'do_rayleigh': True, + 'trayfric': -0.5, # neg. value: time in *days* + 'sponge_pbottom': 150., #Setting the lower pressure boundary for the model sponge layer in Pa. + 'do_conserve_energy': True, + }, + + # FMS Framework configuration + 'diag_manager_nml': { + 'mix_snapshot_average_fields': False # time avg fields are labelled with time in middle of window + }, + + 'fms_nml': { + 'domains_stack_size': 600000 # default: 0 + }, + + 'fms_io_nml': { + 'threading_write': 'single', # default: multi + 'fileset_write': 'single', # default: multi + }, + + 'spectral_dynamics_nml': { + 'damping_order': 4, + 'water_correction_limit': 200.e2, + 'reference_sea_level_press':1.0e5, + 'num_levels':40, #How many model pressure levels to use + 'valid_range_t':[100.,800.], + 'initial_sphum':[2.e-6], + 'vert_coord_option':'uneven_sigma', + 'surf_res':0.2, #Parameter that sets the vertical distribution of sigma levels + 'scale_heights' : 11.0, + 'exponent':7.0, + 'robert_coeff':0.03 + }, + +}) + +#Lets do a run! +if __name__=="__main__": + + cb.compile(debug=False) + #Set up the experiment object, with the first argument being the experiment name. + #This will be the name of the folder that the data will appear in. + + overwrite=False + + exp.run(1, use_restart=False, num_cores=NCORES, overwrite_data=overwrite) + + for i in range(2,121): + exp.run(i, num_cores=NCORES, overwrite_data=overwrite) diff --git a/exp/test_cases/top_down_test/top_down_test.py b/exp/test_cases/top_down_test/top_down_test.py index 75854219a..2b97a71cf 100644 --- a/exp/test_cases/top_down_test/top_down_test.py +++ b/exp/test_cases/top_down_test/top_down_test.py @@ -18,8 +18,6 @@ # is used to load the correct compilers. The env file is always loaded from # $GFDL_BASE and not the checked out git repo. -cb.compile() # compile the source code to working directory $GFDL_WORK/codebase - # create a diagnostics output file for daily snapshots diag = DiagTable() diag.add_file('atmos_daily', 1, 'days', time_units='days') @@ -97,6 +95,7 @@ }) if __name__=="__main__": + cb.compile() # compile the source code to working directory $GFDL_WORK/codebase obls = [15] for obl in obls: diff --git a/exp/test_cases/trip_test/trip_test_command_line b/exp/test_cases/trip_test/trip_test_command_line index fe7f19a87..8b93258ec 100755 --- a/exp/test_cases/trip_test/trip_test_command_line +++ b/exp/test_cases/trip_test/trip_test_command_line @@ -12,7 +12,7 @@ Default settings: """ from trip_test_functions import run_all_tests, list_all_test_cases_implemented_in_trip_test -import argparse +import argparse import sys import pdb diff --git a/exp/test_cases/trip_test/trip_test_functions.py b/exp/test_cases/trip_test/trip_test_functions.py index 8b6fc894b..0820c39f2 100644 --- a/exp/test_cases/trip_test/trip_test_functions.py +++ b/exp/test_cases/trip_test/trip_test_functions.py @@ -13,6 +13,7 @@ import numpy as np import os import sys +import f90nml def get_nml_diag(test_case_name): """Gets the appropriate namelist and input files from each of the test case scripts in the test_cases folder @@ -22,25 +23,29 @@ def get_nml_diag(test_case_name): sys.path.insert(0, os.path.join(GFDL_BASE, 'exp/test_cases/axisymmetric/')) from axisymmetric_test_case import exp as exp_temp input_files = exp_temp.inputfiles - nml_out = exp_temp.namelist + nml_out = exp_temp.namelist + codebase_to_use = IscaCodeBase if 'bucket_model' in test_case_name: sys.path.insert(0, os.path.join(GFDL_BASE, 'exp/test_cases/bucket_hydrology/')) from bucket_model_test_case import exp as exp_temp input_files = exp_temp.inputfiles - nml_out = exp_temp.namelist - + nml_out = exp_temp.namelist + codebase_to_use = IscaCodeBase + if 'frierson' in test_case_name: sys.path.insert(0, os.path.join(GFDL_BASE, 'exp/test_cases/frierson/')) from frierson_test_case import exp as exp_temp input_files = exp_temp.inputfiles - nml_out = exp_temp.namelist + nml_out = exp_temp.namelist + codebase_to_use = IscaCodeBase if 'giant_planet' in test_case_name: sys.path.insert(0, os.path.join(GFDL_BASE, 'exp/test_cases/giant_planet/')) from giant_planet_test_case import exp as exp_temp input_files = exp_temp.inputfiles nml_out = exp_temp.namelist + codebase_to_use = IscaCodeBase #Make giant planet test case a lower resolution so that it runs in a finite time! nml_out['spectral_dynamics_nml']['num_fourier']=42 @@ -54,54 +59,114 @@ def get_nml_diag(test_case_name): from held_suarez_test_case import exp as exp_temp input_files = exp_temp.inputfiles nml_out = exp_temp.namelist + codebase_to_use = IscaCodeBase if 'MiMA' in test_case_name: sys.path.insert(0, os.path.join(GFDL_BASE, 'exp/test_cases/MiMA/')) from MiMA_test_case import exp as exp_temp input_files = exp_temp.inputfiles - nml_out = exp_temp.namelist + nml_out = exp_temp.namelist + codebase_to_use = IscaCodeBase if 'realistic_continents_fixed_sst' in test_case_name: sys.path.insert(0, os.path.join(GFDL_BASE, 'exp/test_cases/realistic_continents/')) from realistic_continents_fixed_sst_test_case import exp as exp_temp input_files = exp_temp.inputfiles nml_out = exp_temp.namelist + codebase_to_use = IscaCodeBase if 'realistic_continents_variable_qflux' in test_case_name: sys.path.insert(0, os.path.join(GFDL_BASE, 'exp/test_cases/realistic_continents/')) from realistic_continents_variable_qflux_test_case import exp as exp_temp input_files = exp_temp.inputfiles nml_out = exp_temp.namelist + codebase_to_use = IscaCodeBase + + if 'soc_realistic_continents_fixed_sst_with_linear_cld_scheme' in test_case_name: + sys.path.insert(0, os.path.join(GFDL_BASE, 'exp/test_cases/simple_clouds/')) + from socrates_aquaplanet import exp as exp_temp + input_files = exp_temp.inputfiles + nml_out = exp_temp.namelist + codebase_to_use=SocratesCodeBase if 'socrates_aquaplanet' in test_case_name: sys.path.insert(0, os.path.join(GFDL_BASE, 'exp/test_cases/socrates_test/')) from socrates_aquaplanet import exp as exp_temp input_files = exp_temp.inputfiles - nml_out = exp_temp.namelist + nml_out = exp_temp.namelist + + if 'socrates_aquaplanet_cloud' in test_case_name: + sys.path.insert(0, os.path.join(GFDL_BASE, 'exp/test_cases/socrates_test/')) + from socrates_aquaplanet_cloud import exp as exp_temp + input_files = exp_temp.inputfiles + nml_out = exp_temp.namelist + codebase_to_use=SocratesCodeBase if 'top_down_test' in test_case_name: sys.path.insert(0, os.path.join(GFDL_BASE, 'exp/test_cases/top_down_test/')) from top_down_test import namelist as nml_out input_files = [] + codebase_to_use = IscaCodeBase if 'variable_co2_grey' in test_case_name: sys.path.insert(0, os.path.join(GFDL_BASE, 'exp/test_cases/variable_co2_concentration/')) from variable_co2_grey import exp as exp_temp input_files = exp_temp.inputfiles - nml_out = exp_temp.namelist + nml_out = exp_temp.namelist + codebase_to_use = IscaCodeBase if 'variable_co2_rrtm' in test_case_name: sys.path.insert(0, os.path.join(GFDL_BASE, 'exp/test_cases/variable_co2_concentration/')) from variable_co2_rrtm import exp as exp_temp input_files = exp_temp.inputfiles - nml_out = exp_temp.namelist + nml_out = exp_temp.namelist + codebase_to_use = IscaCodeBase - return nml_out, input_files + if 'ape_aquaplanet' in test_case_name: + sys.path.insert(0, os.path.join(GFDL_BASE, 'exp/test_cases/ape_aquaplanet/')) + from socrates_ape_aquaplanet_T42 import exp as exp_temp + input_files = exp_temp.inputfiles + nml_out = exp_temp.namelist + codebase_to_use=SocratesCodeBase + + if 'barotropic_vort_eq_stirring' in test_case_name: + sys.path.insert(0, os.path.join(GFDL_BASE, 'exp/test_cases/barotropic_vorticity_equation/')) + from barotropic_vor_eq_stirring_test import exp as exp_temp + from isca import BarotropicCodeBase + input_files = exp_temp.inputfiles + nml_out = exp_temp.namelist + codebase_to_use=BarotropicCodeBase + + if 'shallow_water_stirring' in test_case_name: + sys.path.insert(0, os.path.join(GFDL_BASE, 'exp/test_cases/shallow_water/')) + from shallow_water_stirring_test import exp as exp_temp + from isca import ShallowCodeBase + input_files = exp_temp.inputfiles + nml_out = exp_temp.namelist + codebase_to_use=ShallowCodeBase + + return nml_out, input_files, codebase_to_use def list_all_test_cases_implemented_in_trip_test(): #List of test cases to check - exps_implemented = ['axisymmetric', 'bucket_model', 'frierson', 'giant_planet', 'held_suarez', 'MiMA', 'realistic_continents_fixed_sst', 'realistic_continents_variable_qflux', 'socrates_aquaplanet', 'top_down_test', 'variable_co2_grey', 'variable_co2_rrtm'] + exps_implemented = ['axisymmetric', + 'bucket_model', + 'frierson', + 'giant_planet', + 'held_suarez', + 'MiMA', + 'realistic_continents_fixed_sst', + 'realistic_continents_variable_qflux', + #'simple_clouds', + 'socrates_aquaplanet', + 'socrates_aquaplanet_cloud', + 'top_down_test', + 'variable_co2_grey', + 'variable_co2_rrtm', + 'ape_aquaplanet', + 'barotropic_vort_eq_stirring', + 'shallow_water_stirring'] return exps_implemented @@ -123,6 +188,27 @@ def define_simple_diag_table(): return diag +def define_simple_diag_table_2d(shallow_or_baro): + """Defines a simple diag table for the + shallow water and barotropic vorticity test cases.""" + + if shallow_or_baro=='shallow': + diag_name = 'shallow_diagnostics' + elif shallow_or_baro=='barotropic': + diag_name = 'barotropic_diagnostics' + else: + raise NotImplementedError('incorrect option for 2d diag table') + + diag = DiagTable() + diag.add_file('atmos_daily', 1, 'days', time_units='days') + + #Tell model which diagnostics to write + diag.add_field(diag_name, 'ucomp', time_avg=True) + diag.add_field(diag_name, 'vcomp', time_avg=True) + diag.add_field(diag_name, 'vor', time_avg=True) + + return diag + def process_ids(base_commit_in, later_commit_in): @@ -146,29 +232,41 @@ def conduct_comparison_on_test_case(base_commit, later_commit, test_case_name, r in the diag file. If there are any differences in the output variables then the test classed as a failure.""" data_dir_dict = {} - nml_use, input_files_use = get_nml_diag(test_case_name) - diag_use = define_simple_diag_table() + nml_use, input_files_use, codebase_obj = get_nml_diag(test_case_name) + + if 'shallow_water' in test_case_name: + diag_use = define_simple_diag_table_2d('shallow') + elif 'barotropic_vort_eq' in test_case_name: + diag_use = define_simple_diag_table_2d('barotropic') + else: + diag_use = define_simple_diag_table() + test_pass = True run_complete = True + compile_successful=True #Do the run for each of the commits in turn for s in [base_commit, later_commit]: exp_name = test_case_name+'_trip_test_21_'+s - if 'socrates' in test_case_name: - cb = SocratesCodeBase(repo=repo_to_use, commit=s) - else: - cb = IscaCodeBase(repo=repo_to_use, commit=s) - cb.compile() - exp = Experiment(exp_name, codebase=cb) - exp.namelist = nml_use.copy() - exp.diag_table = diag_use - exp.inputfiles = input_files_use - - #Only run for 3 days to keep things short. - exp.update_namelist({ - 'main_nml': { - 'days': 3, - }}) + cb = codebase_obj(repo=repo_to_use, commit=s) + try: + cb.compile() + exp = Experiment(exp_name, codebase=cb) + exp.namelist = nml_use.copy() + exp.diag_table = diag_use + exp.inputfiles = input_files_use + + #Only run for 3 days to keep things short. + exp.update_namelist({ + 'main_nml': { + 'days': 3, + }}) + except: + run_complete = False + test_pass = False + compile_successful=False + continue + try: # run with a progress bar @@ -180,6 +278,8 @@ def conduct_comparison_on_test_case(base_commit, later_commit, test_case_name, r test_pass = False continue + + data_dir_dict[s] = exp.datadir if run_complete: #For each of the diag files defined, compare the output @@ -196,6 +296,12 @@ def conduct_comparison_on_test_case(base_commit, later_commit, test_case_name, r print('Test failed for '+var+' max diff value = '+str(maxval.values)) test_pass = False + base_experiment_input_nml = f90nml.read(data_dir_dict[base_commit] +'/run0001/input.nml') + later_commit_input_nml = f90nml.read(data_dir_dict[later_commit] +'/run0001/input.nml') + + if base_experiment_input_nml!=later_commit_input_nml: + raise AttributeError(f'The two experiments to be compared have been run using different input namelists, and so the results may be different because of this. This only happens when you have run the trip tests using one of the commit IDs before, and that you happen to have used a different version of the test cases on that previous occasion. Try removing both {data_dir_dict[base_commit]} and {data_dir_dict[later_commit]} and try again.') + if test_pass: print('Test passed for '+test_case_name+'. Commit '+later_commit+' gives the same answer as commit '+base_commit) return_test_result = 'pass' @@ -204,7 +310,12 @@ def conduct_comparison_on_test_case(base_commit, later_commit, test_case_name, r return_test_result = 'fail' else: - print('Test failed for '+test_case_name+' because the run crashed.') + if compile_successful: + #This means that the compiles were both successful, but at least one of the runs crashed. + print('Test failed for '+test_case_name+' because the run crashed.') + else: + print('Test failed for '+test_case_name+' because at least one of the runs failed to compile.') + return_test_result = 'fail' @@ -239,4 +350,4 @@ def run_all_tests(base_commit, later_commit, exps_to_check, repo_to_use='git@git for exp_name in exps_to_check: exp_outcome_dict[exp_name] = conduct_comparison_on_test_case(base_commit, later_commit, exp_name, repo_to_use = repo_to_use, num_cores_to_use=num_cores_to_use) - output_results_function(exp_outcome_dict, base_commit, later_commit) \ No newline at end of file + output_results_function(exp_outcome_dict, base_commit, later_commit) diff --git a/exp/test_cases/variable_co2_concentration/variable_co2_grey.py b/exp/test_cases/variable_co2_concentration/variable_co2_grey.py index 4cdcab304..fb3b1eb0c 100644 --- a/exp/test_cases/variable_co2_concentration/variable_co2_grey.py +++ b/exp/test_cases/variable_co2_concentration/variable_co2_grey.py @@ -19,8 +19,6 @@ # is used to load the correct compilers. The env file is always loaded from # $GFDL_BASE and not the checked out git repo. -cb.compile() # compile the source code to working directory $GFDL_WORK/codebase - # create an Experiment object to handle the configuration of model parameters # and output diagnostics exp = Experiment('variable_co2_grey_test_experiment', codebase=cb) @@ -172,6 +170,8 @@ #Lets do a run! if __name__=="__main__": + cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + exp.run(1, use_restart=False, num_cores=NCORES) for i in range(2,121): exp.run(i, num_cores=NCORES) \ No newline at end of file diff --git a/exp/test_cases/variable_co2_concentration/variable_co2_rrtm.py b/exp/test_cases/variable_co2_concentration/variable_co2_rrtm.py index 9ba4173bc..228cb46a6 100644 --- a/exp/test_cases/variable_co2_concentration/variable_co2_rrtm.py +++ b/exp/test_cases/variable_co2_concentration/variable_co2_rrtm.py @@ -19,8 +19,6 @@ # is used to load the correct compilers. The env file is always loaded from # $GFDL_BASE and not the checked out git repo. -cb.compile() # compile the source code to working directory $GFDL_WORK/codebase - # create an Experiment object to handle the configuration of model parameters # and output diagnostics exp = Experiment('variable_co2_rrtm_test_experiment', codebase=cb) @@ -171,6 +169,8 @@ #Lets do a run! if __name__=="__main__": + cb.compile() # compile the source code to working directory $GFDL_WORK/codebase + exp.run(1, use_restart=False, num_cores=NCORES) for i in range(2,121): exp.run(i, num_cores=NCORES) diff --git a/postprocessing/plevel_interpolation/scripts/run_plevel.py b/postprocessing/plevel_interpolation/scripts/run_plevel.py index 294ecb53a..6b6b9bfe7 100644 --- a/postprocessing/plevel_interpolation/scripts/run_plevel.py +++ b/postprocessing/plevel_interpolation/scripts/run_plevel.py @@ -7,11 +7,12 @@ import subprocess start_time=time.time() -base_dir='/scratch/sit204/Data_2013/' -exp_name_list = ['no_ice_flux_lhe_exps_q_flux_hadgem_anoms_3'] +base_dir='/disca/share/sit204/data_from_isca_cpu/cssp_perturb_exps/anoms/' +#exp_name_list = ['soc_ga3_files_smooth_topo_fftw_mk1_fresh_compile_long', 'soc_ga3_files_smooth_topo_old_fft_mk2_long'] +exp_name_list = [f'soc_ga3_do_simple_false_cmip_o3_bucket_perturbed_ens_{f}' for f in range(100, 200)] avg_or_daily_list=['monthly'] -start_file=287 -end_file=288 +start_file=1 +end_file=1 nfiles=(end_file-start_file)+1 do_extra_averaging=False #If true, then 6hourly data is averaged into daily data using cdo @@ -44,7 +45,7 @@ var_names['timestep']='-a' var_names['6hourly']='ucomp slp height vor t_surf vcomp omega' var_names['daily']='ucomp slp height vor t_surf vcomp omega temp' - file_suffix='_interp_new_height_temp' + file_suffix='_interp_new_height_temp_not_below_ground' elif level_set=='ssw_diagnostics': plevs['6hourly']=' -p "1000 10000"' @@ -66,10 +67,12 @@ number_prefix='' - if n+start_file < 100: + if n+start_file < 1000: number_prefix='0' - if n+start_file < 10: + if n+start_file < 100: number_prefix='00' + if n+start_file < 10: + number_prefix = '000' nc_file_in = base_dir+'/'+exp_name+'/run'+number_prefix+str(n+start_file)+'/atmos_'+avg_or_daily+'.nc' nc_file_out = out_dir+'/'+exp_name+'/run'+number_prefix+str(n+start_file)+'/atmos_'+avg_or_daily+file_suffix+'.nc' diff --git a/src/atmos_column/column.F90 b/src/atmos_column/column.F90 new file mode 100644 index 000000000..c059f0fdf --- /dev/null +++ b/src/atmos_column/column.F90 @@ -0,0 +1,804 @@ +module column_mod + + +#ifdef INTERNAL_FILE_NML +use mpp_mod, only: input_nml_file +#else +use fms_mod, only: open_namelist_file +#endif + +use constants_mod, only: rdgas, rvgas, pi, grav +use column_init_cond_mod, only: column_init_cond +use column_grid_mod, only: column_grid_init, get_deg_lat, get_deg_lon, get_grid_boundaries, get_sin_lat, area_weighted_global_mean +use diag_manager_mod, only: diag_axis_init, register_diag_field, register_static_field, send_data, diag_manager_end +use field_manager_mod, only: MODEL_ATMOS +use fms_mod, only: mpp_pe, mpp_root_pe, error_mesg, NOTE, FATAL, write_version_number, stdlog, & + close_file, open_restart_file, file_exist, set_domain, & + read_data, write_data, check_nml_error, lowercase, uppercase, mpp_npes, & + field_size +use mpp_mod, only: NULL_PE, mpp_transmit, mpp_sync, mpp_send, & + mpp_broadcast, mpp_recv, mpp_max +use press_and_geopot_mod, only: press_and_geopot_init, pressure_variables, & + compute_pressures_and_heights,press_and_geopot_end +use tracer_manager_mod, only: get_number_tracers, query_method, get_tracer_index, NO_TRACER, get_tracer_names +use spec_mpp_mod, only: spec_mpp_init, get_grid_domain, grid_domain +use time_manager_mod, only: time_type, get_time, set_time, get_calendar_type, NO_CALENDAR, & + get_date, interval_alarm, operator( - ), operator( + ) +use tracer_type_mod, only: tracer_type, tracer_type_version, tracer_type_tagname + + +implicit none +private + +public :: column_init, column, column_end, column_diagnostics, get_num_levels, get_surf_geopotential, get_initial_fields, get_axis_id + +character(len=128), parameter :: version = '$Id: column.F90,v 0.1 2018/14/11 HH:MM:SS isca Exp $' +character(len=128), parameter :: tagname = '$Name: isca_201811 $' + +integer :: id_ps, id_u, id_v, id_t +integer :: id_pres_full, id_pres_half, id_zfull, id_zhalf +integer, allocatable, dimension(:) :: id_tr +character(len=8) :: mod_name = 'column' +integer, dimension(4) :: axis_id + +integer, parameter :: num_time_levels = 2 +logical :: module_is_initialized = .false. +logical :: dry_model + +type(time_type) :: Time_step, Alarm_time, Alarm_interval ! Used to determine when it is time to print global integrals. + +real, allocatable, dimension(:) :: sin_lat +real, allocatable, dimension(:,:,:,: ) :: ug, vg, tg ! last dimension is for time level +real, allocatable, dimension(:,:,:,:,:) :: grid_tracers ! 4'th dimension is for time level, last dimension is for tracer number +real, allocatable, dimension(:,: ) :: surf_geopotential +real, allocatable, dimension(:,:, : ) :: psg +real, allocatable, dimension(:) :: pk, bk + +! for lon boundaries NTL START HERE !!! +!real, allocatable, dimension(:) :: lat_boundaries_global, lon_boundaries_global + +real :: virtual_factor, dt_real +integer :: pe, npes, num_tracers, nhum, step_number +integer :: is, ie, js, je +integer :: previous, current, future + +!! NAMELIST VARIABLES + +logical :: use_virtual_temperature= .false., & + graceful_shutdown = .false. + +integer :: lon_max = 1, & ! Column + lat_max = 1, & + num_fourier = 0, & + num_spherical = 0, & + num_levels = 31, & + num_steps = 1 + +integer, dimension(2) :: print_interval=(/1,0/) + +logical :: q_decrease_only = .false. + +character(len=64) :: vert_coord_option = 'even_sigma', & + vert_difference_option = 'simmons_and_burridge', & + initial_state_option = 'default' + + +real :: scale_heights = 4., & + reference_sea_level_press = 101325., & + surf_res = .1, & + p_press = .1, & + p_sigma = .3, & + exponent = 2.5, & + initial_sphum = 0.0, & + robert_coeff = 0.0, & + raw_filter_coeff = 1.0 + +logical :: json_logging = .false. + +real, dimension(2) :: valid_range_t = (/100.,500./) + +namelist /column_nml/ use_virtual_temperature, valid_range_t, & + lon_max, lat_max, num_levels, & + print_interval, vert_coord_option, & + vert_difference_option, use_virtual_temperature, & + reference_sea_level_press, scale_heights, surf_res, & + p_press, p_sigma, exponent, & + initial_state_option, initial_sphum, graceful_shutdown, & + raw_filter_coeff, robert_coeff, json_logging, q_decrease_only + + +contains + +subroutine column_init(Time, Time_step_in, tracer_attributes, dry_model_out, nhum_out) + + type(time_type), intent(in) :: Time, Time_step_in + type(tracer_type), intent(inout), dimension(:) :: tracer_attributes + logical, intent(out) :: dry_model_out + integer, intent(out) :: nhum_out + + integer :: unit, ierr, io, ntr, nsphum, nmix_rat, seconds, days + !real :: del_lon, del_lat !!! NTL START HERE + !real :: longitude_origin_local = 0.0 + + integer :: i, j + + character(len=32) :: params + character(len=128) :: tname, longname, units + character(len=8) :: err_msg_1 + + if(module_is_initialized) return + +#ifdef INTERNAL_FILE_NML + read (input_nml_file, nml=column_nml, iostat=io) + ierr = check_nml_error(io, 'column_nml') +#else + unit = open_namelist_file() + ierr=1 + do while (ierr /= 0) + read(unit, nml=column_nml, iostat=io, end=20) + ierr = check_nml_error (io, 'column_nml') + enddo + 20 call close_file (unit) +#endif + + pe = mpp_pe() + npes = mpp_npes() + if (npes .gt. 1) then + write(err_msg_1,'(i8)') npes + call error_mesg('column_init','Can only run column model on one processor but npes = '//err_msg_1, FATAL) + endif + + call write_version_number(version, tagname) + if(mpp_pe() == mpp_root_pe()) write (stdlog(), nml=column_nml) + call write_version_number(tracer_type_version, tracer_type_tagname) + + Time_step = Time_step_in + Alarm_interval = set_time(print_interval(2), print_interval(1)) + Alarm_time = Time + Alarm_interval + + + call spec_mpp_init( num_fourier, num_spherical, lon_max, lat_max ) + + !!! MAYBE PUT ALL OF THIS IN A FILE LIKE: + call column_grid_init(lon_max, lat_max) ! and then get to it with other functions + call get_grid_domain(is, ie, js, je) + call get_number_tracers(MODEL_ATMOS, num_prog=num_tracers) + call allocate_fields + + do ntr=1,num_tracers + + call get_tracer_names(MODEL_ATMOS, ntr, tname, longname, units) + tracer_attributes(ntr)%name = lowercase(tname) + + enddo + nsphum = get_tracer_index(MODEL_ATMOS, 'sphum') + nmix_rat = get_tracer_index(MODEL_ATMOS, 'mix_rat') + + if(nsphum == NO_TRACER) then + if(nmix_rat == NO_TRACER) then + nhum = 0 + dry_model = .true. + else + nhum = nmix_rat + dry_model = .false. + endif + else + if(nmix_rat == NO_TRACER) then + nhum = nsphum + dry_model = .false. + else + call error_mesg('column_init','sphum and mix_rat cannot both be specified as tracers at the same time', FATAL) + endif + endif + dry_model_out = dry_model + nhum_out = nhum + + + call read_restart_or_do_coldstart(tracer_attributes) + + call press_and_geopot_init(pk, bk, use_virtual_temperature, vert_difference_option) + call column_diagnostics_init(Time) + + if(use_virtual_temperature) then + virtual_factor = (rvgas/rdgas) - 1.0 + end if + + allocate(sin_lat(js:je)) + call get_sin_lat(sin_lat) + + call set_domain(grid_domain) + call get_time(Time_step, seconds, days) + dt_real = 86400*days + seconds + + module_is_initialized = .true. + ! NTL: CHECK AGAINST spectral_dynamics_init TO SEE WHAT ELSE NEEDS TO BE INITIALISED + return +end subroutine column_init + +subroutine column(Time, psg_final, ug_final, vg_final, tg_final, tracer_attributes, grid_tracers_final, & + time_level_out, dt_psg, dt_ug, dt_vg, dt_tg, dt_tracers, wg_full, p_full, p_half, z_full) + +type(time_type), intent(in) :: Time +real, intent(out), dimension(is:, js: ) :: psg_final +real, intent(out), dimension(is:, js:, : ) :: ug_final, vg_final, tg_final +real, intent(out), dimension(is:, js:, :,:,:) :: grid_tracers_final +type(tracer_type),intent(inout), dimension(:) :: tracer_attributes +integer, intent(in) :: time_level_out + +real, intent(inout), dimension(is:, js: ) :: dt_psg +real, intent(inout), dimension(is:, js:, : ) :: dt_ug, dt_vg, dt_tg +real, intent(inout), dimension(is:, js:, :, :) :: dt_tracers +real, intent(out), dimension(is:, js:, : ) :: wg_full, p_full +real, intent(out), dimension(is:, js:, : ) :: p_half +real, intent(in), dimension(is:, js:, : ) :: z_full + +type(time_type) :: Time_diag + +real, dimension(is:ie, js:je, num_levels, num_tracers) :: dt_tracers_tmp +integer :: p, seconds, days +real :: delta_t +real :: extrtmp +integer :: ii,jj,kk,i1,j1,k1 +integer :: ntr + +logical :: pe_is_valid = .true. +logical :: r_pe_is_valid = .true. + +! THIS IS WHERE I NEED TO START FROM... +! TO DO: LOCAL VARIABLES, CHECK INPUTS, HOOK UP TO ATMOSPHERE.F90 AND GLOBAL VARIABLE DEFINTIONS +! DO something simple like next = current + dt_var * timestep + +if(.not.module_is_initialized) then + call error_mesg('column','column has not been initialized ', FATAL) +endif + +dt_tracers_tmp = dt_tracers + +step_loop: do step_number=1,num_steps + +if(previous == current) then + delta_t = dt_real/num_steps +else + delta_t = 2*dt_real/num_steps +endif +if(num_time_levels == 2) then + future = 3 - current +else + call error_mesg('column','Do not know how to set time pointers when num_time_levels does not equal 2',FATAL) +endif + + +call leapfrog_3d_real(tg, dt_tg, previous, current, future, delta_t, robert_coeff, raw_filter_coeff, .false.) + + + + +if(minval(tg(:,:,:,future)) < valid_range_t(1) .or. maxval(tg(:,:,:,future)) > valid_range_t(2)) then + pe_is_valid = .false. +!mj !s This doesn't affect the normal running of the code in any way. It simply allows identification of the point where temp violation has occured. + if(minval(tg(:,:,:,future)) < valid_range_t(1))then + extrtmp = minval(tg(:,:,:,future)) + else + extrtmp = maxval(tg(:,:,:,future)) + endif + do k1=1,size(tg,3) + do j1=1,size(tg,2) + do i1=1,size(tg,1) + if(tg(i1,j1,k1,future) .eq. extrtmp)then + ii=i1 + jj=j1 + kk=k1 + exit + endif + enddo + enddo + enddo + write(*,'(a,i3,a,3i3,2f10.3)')'PE, location, Textr(curr,future): ',mpp_pe()& + &,': ',ii,jj,kk& + &,tg(ii,jj,kk,current)& + &,tg(ii,jj,kk,future) + write(*,'(a,i3,a,3i3,2f10.3)')'PE, location, Uextr(curr,future): ',mpp_pe()& + &,': ',ii,jj,kk& + &,ug(ii,jj,kk,current)& + &,ug(ii,jj,kk,future) +!jm + if (.not.graceful_shutdown) then + call error_mesg('column','temperatures out of valid range', FATAL) + endif +endif + +! synchronisation between nodes. THIS WILL SLOW DOWN THE RUN but ensures +! all partially complete diagnostics are written to netcdf output. +if (graceful_shutdown) then + if (pe == mpp_root_pe()) then + do p = 0, npes-1 + ! wait for all nodes to report they are error free + if (p.ne.pe) then + call mpp_recv(r_pe_is_valid, p) + if (.not.r_pe_is_valid .or. .not.pe_is_valid) then + ! node reports error, tell all the others to shutdown + r_pe_is_valid = .false. + exit + end if + end if + end do + ! tell all the nodes to continue, or not + do p =0, npes-1 + if (p.ne.pe) call mpp_send(r_pe_is_valid, p) + end do + else + call mpp_send(pe_is_valid, mpp_root_pe()) + ! wait to hear back from root that all are ok to continue + call mpp_recv(r_pe_is_valid, mpp_root_pe()) + endif + if (.not.r_pe_is_valid) then + ! one of the nodes has broken the condition. gracefully shutdown diagnostics + ! and then raise a fatal error after all have hit the sync point. + call diag_manager_end(Time) + call mpp_sync() + call error_mesg('column','temperatures out of valid range', FATAL) + endif +endif + +! NTL: Need to write a different version of this which uses the leapfrog below... +do ntr = 1, num_tracers + call leapfrog_3d_real(grid_tracers(:,:,:,:,ntr),dt_tracers_tmp(:,:,:,ntr),previous,current,future,delta_t,tracer_attributes(ntr)%robert_coeff, raw_filter_coeff, q_decrease_only) +enddo + + +previous = current +current = future + +call get_time(Time, seconds, days) +seconds = seconds + step_number*int(dt_real/2) +Time_diag = set_time(seconds, days) + +enddo step_loop + +psg_final = psg(:,:, previous) +ug_final = ug(:,:,:,previous) +vg_final = vg(:,:,:,previous) +tg_final = tg(:,:,:,current) +grid_tracers_final(:,:,:,time_level_out,:) = grid_tracers(:,:,:,current,:) + +return +end subroutine column + +subroutine column_diagnostics_init(Time) + + type(time_type), intent(in) :: Time + real, dimension(lon_max ) :: lon + real, dimension(lon_max+1) :: lonb + real, dimension(lat_max ) :: lat + real, dimension(lat_max+1) :: latb + real, dimension(num_levels) :: p_full, ln_p_full + real, dimension(num_levels+1) :: p_half, ln_p_half + integer, dimension(3) :: axes_3d_half, axes_3d_full + integer :: id_lonb, id_latb, id_phalf, id_lon, id_lat, id_pfull + integer :: id_pk, id_bk, id_zsurf, ntr + real :: rad_to_deg + logical :: used + real,dimension(2) :: vrange + character(len=128) :: tname, longname, units + + ! NTL: NEED TO DO THIS + + vrange = (/ -400., 400. /) + + rad_to_deg = 180./pi + call get_grid_boundaries(lonb,latb,global=.true.) + call get_deg_lon(lon) + call get_deg_lat(lat) + + id_lonb=diag_axis_init('lonb', rad_to_deg*lonb, 'degrees_E', 'x', 'longitude edges', set_name=mod_name, Domain2=grid_domain) + id_latb=diag_axis_init('latb', rad_to_deg*latb, 'degrees_N', 'y', 'latitude edges', set_name=mod_name, Domain2=grid_domain) + id_lon =diag_axis_init('lon', lon, 'degrees_E', 'x', 'longitude', set_name=mod_name, Domain2=grid_domain, edges=id_lonb) + id_lat =diag_axis_init('lat', lat, 'degrees_N', 'y', 'latitude', set_name=mod_name, Domain2=grid_domain, edges=id_latb) + + call pressure_variables(p_half, ln_p_half, p_full, ln_p_full, reference_sea_level_press) + p_half = .01*p_half + p_full = .01*p_full + id_phalf = diag_axis_init('phalf',p_half,'hPa','z','approx half pressure level',direction=-1,set_name=mod_name) + id_pfull = diag_axis_init('pfull',p_full,'hPa','z','approx full pressure level',direction=-1,set_name=mod_name,edges=id_phalf) + + axes_3d_half = (/ id_lon, id_lat, id_phalf /) + axes_3d_full = (/ id_lon, id_lat, id_pfull /) + axis_id(1) = id_lon + axis_id(2) = id_lat + axis_id(3) = id_pfull + axis_id(4) = id_phalf + + id_pk = register_static_field(mod_name, 'pk', (/id_phalf/), 'vertical coordinate pressure values', 'pascals') + id_bk = register_static_field(mod_name, 'bk', (/id_phalf/), 'vertical coordinate sigma values', 'none') + id_zsurf = register_static_field(mod_name, 'zsurf', (/id_lon,id_lat/), 'geopotential height at the surface', 'm') + + if(id_pk > 0) used = send_data(id_pk, pk, Time) + if(id_bk > 0) used = send_data(id_bk, bk, Time) + if(id_zsurf > 0) used = send_data(id_zsurf, surf_geopotential/grav, Time) + + id_ps = register_diag_field(mod_name, & + 'ps', (/id_lon,id_lat/), Time, 'surface pressure', 'pascals') + + id_u = register_diag_field(mod_name, & + 'ucomp', axes_3d_full, Time, 'zonal wind component', 'm/sec', range=vrange) + + id_v = register_diag_field(mod_name, & + 'vcomp', axes_3d_full, Time, 'meridional wind component', 'm/sec', range=vrange) + + id_t = register_diag_field(mod_name, & + 'temp', axes_3d_full, Time, 'temperature', 'deg_k', range=valid_range_t) + + id_pres_full = register_diag_field(mod_name, & + 'pres_full', axes_3d_full, Time, 'pressure at full model levels', 'pascals') + + id_pres_half = register_diag_field(mod_name, & + 'pres_half', axes_3d_half, Time, 'pressure at half model levels', 'pascals') + + id_zfull = register_diag_field(mod_name, & + 'height', axes_3d_full, Time, 'geopotential height at full model levels','m') + + id_zhalf = register_diag_field(mod_name, & + 'height_half', axes_3d_half, Time, 'geopotential height at half model levels','m') + + allocate(id_tr(num_tracers)) + do ntr=1,num_tracers + call get_tracer_names(MODEL_ATMOS, ntr, tname, longname, units) + id_tr(ntr) = register_diag_field(mod_name, tname, axes_3d_full, Time, longname, units) + enddo + + return +end subroutine column_diagnostics_init + + + + +subroutine column_diagnostics(Time, p_surf, u_grid, v_grid, t_grid, wg_full, tr_grid, time_level) + + type(time_type), intent(in) :: Time + real, intent(in), dimension(is:, js:) :: p_surf + real, intent(in), dimension(is:, js:, :) :: u_grid, v_grid, t_grid, wg_full + real, intent(in), dimension(is:, js:, :, :, :) :: tr_grid + integer, intent(in) :: time_level + + real, dimension(is:ie, js:je, num_levels) :: ln_p_full, p_full, z_full + real, dimension(is:ie, js:je, num_levels+1) :: ln_p_half, p_half, z_half + logical :: used + integer :: ntr, i, j, k + character(len=8) :: err_msg_1, err_msg_2 + + if(id_ps > 0) used = send_data(id_ps, p_surf, Time) + if(id_u > 0) used = send_data(id_u, u_grid, Time) + if(id_v > 0) used = send_data(id_v, v_grid, Time) + if(id_t > 0) used = send_data(id_t, t_grid, Time) + + if(id_zfull > 0 .or. id_zhalf > 0) then + call compute_pressures_and_heights(t_grid, p_surf, surf_geopotential, z_full, z_half, p_full, p_half) + else if(id_pres_half > 0 .or. id_pres_full > 0) then + call pressure_variables(p_half, ln_p_half, p_full, ln_p_full, p_surf) + endif + + if(id_zfull > 0) used = send_data(id_zfull, z_full, Time) + if(id_zhalf > 0) used = send_data(id_zhalf, z_half, Time) + if(id_pres_full>0) used = send_data(id_pres_full, p_full, Time) + if(id_pres_half>0) used = send_data(id_pres_half, p_half, Time) + + if(size(tr_grid,5) /= num_tracers) then + write(err_msg_1,'(i8)') size(tr_grid,5) + write(err_msg_2,'(i8)') num_tracers + call error_mesg('column_diagnostics','size(tracers)='//err_msg_1//' Should be='//err_msg_2, FATAL) + endif + do ntr=1,num_tracers + if(id_tr(ntr) > 0) used = send_data(id_tr(ntr), tr_grid(:,:,:,time_level,ntr), Time) + enddo + + + if(interval_alarm(Time, Time_step, Alarm_time, Alarm_interval)) then + call global_integrals(Time, p_surf, u_grid, v_grid, t_grid, wg_full, tr_grid(:,:,:,time_level,:)) + endif + + return + end subroutine column_diagnostics + + subroutine column_end(tracer_attributes, Time) + + type(tracer_type), intent(in), dimension(:) :: tracer_attributes + type(time_type), intent(in), optional :: Time + integer :: ntr, nt + character(len=64) :: file, tr_name + + if(.not.module_is_initialized) return + + file='RESTART/column_model.res' + call write_data(trim(file), 'previous', previous, no_domain=.true.) + call write_data(trim(file), 'current', current, no_domain=.true.) + call write_data(trim(file), 'pk', pk, no_domain=.true.) + call write_data(trim(file), 'bk', bk, no_domain=.true.) + do nt=1,num_time_levels + call write_data(trim(file), 'ug', ug(:,:,:,nt), grid_domain) + call write_data(trim(file), 'vg', vg(:,:,:,nt), grid_domain) + call write_data(trim(file), 'tg', tg(:,:,:,nt), grid_domain) + call write_data(trim(file), 'psg', psg(:,:, nt), grid_domain) + do ntr = 1,num_tracers + tr_name = trim(tracer_attributes(ntr)%name) + call write_data(trim(file), trim(tr_name), grid_tracers(:,:,:,nt,ntr), grid_domain) + enddo + enddo + call write_data(trim(file), 'surf_geopotential', surf_geopotential, grid_domain) + + deallocate(ug, vg, tg, psg) + deallocate(sin_lat) + deallocate(pk, bk) + deallocate(surf_geopotential) + deallocate(grid_tracers) + + call column_diagnostics_end + call press_and_geopot_end + call set_domain(grid_domain) + module_is_initialized = .false. + + return + end subroutine column_end + + subroutine column_diagnostics_end + + if(.not.module_is_initialized) return + + deallocate(id_tr) + + return + end subroutine column_diagnostics_end + + subroutine global_integrals(Time, p_surf, u_grid, v_grid, t_grid, wg_full, tr_grid) + type(time_type), intent(in) :: Time + real, intent(in), dimension(is:ie, js:je) :: p_surf + real, intent(in), dimension(is:ie, js:je, num_levels) :: u_grid, v_grid, t_grid, wg_full + real, intent(in), dimension(is:ie, js:je, num_levels, num_tracers) :: tr_grid + integer :: year, month, days, hours, minutes, seconds + character(len=4), dimension(12) :: month_name + + real, dimension(is:ie, js:je, num_levels) :: speed + real :: max_speed, avgT + + month_name=(/' Jan',' Feb',' Mar',' Apr',' May',' Jun',' Jul',' Aug',' Sep',' Oct',' Nov',' Dec'/) + + speed = sqrt(u_grid*u_grid + v_grid*v_grid) + max_speed = maxval(speed) + call mpp_max(max_speed) + + avgT = area_weighted_global_mean(t_grid(:,:, num_levels)) + + if(mpp_pe() == mpp_root_pe()) then + if(get_calendar_type() == NO_CALENDAR) then + call get_time(Time, seconds, days) + if (json_logging) then + write(*, 300) days, seconds, max_speed, avgT + else + write(*,100) days, seconds + end if + else + call get_date(Time, year, month, days, hours, minutes, seconds) + if (json_logging) then + write(*,400) year, month, days, hours, minutes, seconds, max_speed, avgT + else + write(*,200) year, month_name(month), days, hours, minutes, seconds + end if + endif + endif + 100 format(' Integration completed through',i6,' days',i6,' seconds') + 200 format(' Integration completed through',i5,a4,i3,2x,i2,':',i2,':',i2) + 300 format(1x, '{"day":',i6,2x,',"second":', i6, & + 2x,',"max_speed":',e13.6,3x,',"avg_T":',e13.6, 3x '}') + 400 format(1x, '{"date": "',i0.4,'-',i0.2,'-',i0.2, & + '", "time": "', i0.2,':', i0.2,':', i0.2, '", "max_speed":',f6.1,3x,',"avg_T":',f6.1, 3x '}') + + end subroutine global_integrals + + + + +subroutine allocate_fields + + allocate (psg (is:ie, js:je, num_time_levels)) + allocate (ug (is:ie, js:je, num_levels, num_time_levels)) + allocate (vg (is:ie, js:je, num_levels, num_time_levels)) + allocate (tg (is:ie, js:je, num_levels, num_time_levels)) + + allocate (pk(num_levels+1), bk(num_levels+1)) + + allocate (surf_geopotential(is:ie, js:je)) + + allocate (grid_tracers(is:ie, js:je, num_levels, num_time_levels, num_tracers)) + + ! Filling allocatable arrays with zeros immediately after allocation facilitates code debugging + psg=0.; ug=0.; vg=0.; tg=0. + pk=0.; bk=0.; surf_geopotential=0.; grid_tracers=0. + + return +end subroutine allocate_fields + +subroutine get_num_levels(num_levels_out) + integer, intent(out) :: num_levels_out + + if(.not.module_is_initialized) then + call error_mesg('get_num_levels', 'column_init has not been called.', FATAL) + endif + + num_levels_out = num_levels + + return +end subroutine get_num_levels + +subroutine get_surf_geopotential(surf_geopotential_out) + real, intent(out), dimension(:,:) :: surf_geopotential_out + character(len=64) :: chtmp='shape(surf_geopotential)= should be ' + + if(.not.module_is_initialized) then + call error_mesg('get_surf_geopotential', 'column_init has not been called.', FATAL) + endif + + if(any(shape(surf_geopotential_out) /= shape(surf_geopotential))) then + write(chtmp(26:37),'(3i4)') shape(surf_geopotential_out) + write(chtmp(50:61),'(3i4)') shape(surf_geopotential) + call error_mesg('get_surf_geopotential', 'surf_geopotential has wrong shape. '//chtmp, FATAL) + endif + + surf_geopotential_out = surf_geopotential + + return +end subroutine get_surf_geopotential + + + +subroutine read_restart_or_do_coldstart(tracer_attributes) + + ! For backward compatibility, this routine has the capability + ! to read native data restart files written by inchon code. + + type(tracer_type), intent(inout), dimension(:) :: tracer_attributes + + integer :: m, n, k, nt, ntr + integer, dimension(4) :: siz + character(len=64) :: file, tr_name + character(len=4) :: ch1,ch2,ch3,ch4,ch5,ch6 + + file = 'INPUT/column_model.res.nc' + if(file_exist(trim(file))) then + call field_size(trim(file), 'ug', siz) + if(lon_max /= siz(1) .or. lat_max /= siz(2)) then + write(ch1,'(i4)') siz(1) + write(ch2,'(i4)') siz(2) + write(ch3,'(i4)') lon_max + write(ch4,'(i4)') lat_max + call error_mesg('column_init','Resolution of restart data does not match resolution specified on namelist.'// & + ' Restart data: lon_max='//ch1//', lat_max='//ch2//' Namelist: lon_max='//ch3//', lat_max='//ch4, FATAL) + endif + call read_data(trim(file), 'previous', previous, no_domain=.true.) + call read_data(trim(file), 'current', current, no_domain=.true.) + call read_data(trim(file), 'pk', pk, no_domain=.true.) + call read_data(trim(file), 'bk', bk, no_domain=.true.) + do nt=1,num_time_levels + call read_data(trim(file), 'ug', ug(:,:,:,nt), grid_domain, timelevel=nt) + call read_data(trim(file), 'vg', vg(:,:,:,nt), grid_domain, timelevel=nt) + call read_data(trim(file), 'tg', tg(:,:,:,nt), grid_domain, timelevel=nt) + call read_data(trim(file), 'psg', psg(:,:, nt), grid_domain, timelevel=nt) + do ntr = 1,num_tracers + tr_name = trim(tracer_attributes(ntr)%name) + call read_data(trim(file), trim(tr_name), grid_tracers(:,:,:,nt,ntr), grid_domain, timelevel=nt) + enddo ! loop over tracers + enddo ! loop over time levels + call read_data(trim(file), 'surf_geopotential', surf_geopotential, grid_domain) + + else + do ntr = 1,num_tracers + if(trim(tracer_attributes(ntr)%name) == 'sphum') then + grid_tracers(:,:,:,:,ntr) = initial_sphum + else if(trim(tracer_attributes(ntr)%name) == 'mix_rat') then + grid_tracers(:,:,:,:,ntr) = 0. + else + grid_tracers(:,:,:,:,ntr) = 0. + endif + enddo + + previous = 1 + current = 1 + call column_init_cond(initial_state_option, tracer_attributes, reference_sea_level_press, use_virtual_temperature,& + vert_coord_option, vert_difference_option, scale_heights, surf_res, p_press, p_sigma, & + exponent, pk, bk, ug(:,:,:,1), vg(:,:,:,1), tg(:,:,:,1), psg(:,:,1), & + grid_tracers(:,:,:,1,:), surf_geopotential) ! NTL REMOVED LAT AND LON BOUNDARIES + + ug (:,:,:,2) = ug (:,:,:,1) + vg (:,:,:,2) = vg (:,:,:,1) + tg (:,:,:,2) = tg (:,:,:,1) + psg (:,:, 2) = psg (:,:, 1) + grid_tracers(:,:,:,2,:) = grid_tracers(:,:,:,1,:) + + + endif + + return +end subroutine read_restart_or_do_coldstart + +subroutine get_initial_fields(ug_out, vg_out, tg_out, psg_out, grid_tracers_out) + real, intent(out), dimension(:,:,:) :: ug_out, vg_out, tg_out + real, intent(out), dimension(:,:) :: psg_out + real, intent(out), dimension(:,:,:,:) :: grid_tracers_out + + if(.not.module_is_initialized) then + call error_mesg('column, get_initial_fields','column has not been initialized',FATAL) + endif + + if(previous /= 1 .or. current /= 1) then + call error_mesg('column, get_initial_fields','This routine may be called only to get the& + & initial values after a cold_start',FATAL) + endif + + ug_out = ug(:,:,:,1) + vg_out = vg(:,:,:,1) + tg_out = tg(:,:,:,1) + psg_out = psg(:,:, 1) + grid_tracers_out = grid_tracers(:,:,:,1,:) + + end subroutine get_initial_fields + + function get_axis_id() + integer, dimension(4) :: get_axis_id + + if(.not.module_is_initialized) then + call error_mesg('get_axis_id','column_diagnostics_init has not been called.', FATAL) + endif + get_axis_id = axis_id + return + end function get_axis_id + + + + + subroutine leapfrog_3d_real(a, dt_a, previous, current, future, delta_t, robert_coeff, raw_filter_coeff, q_decrease_only) + + real, intent(inout), dimension(:,:,:,:) :: a + real, intent(in), dimension(:,:,: ) :: dt_a + integer, intent(in) :: previous, current, future + real, intent(in) :: delta_t, robert_coeff, raw_filter_coeff + logical, intent(in) :: q_decrease_only + + real, dimension(size(dt_a,1),size(dt_a,2),size(dt_a,3)) :: prev_curr_part_raw_filter + + integer :: i,j,k + + + prev_curr_part_raw_filter=a(:,:,:,previous) - 2.0*a(:,:,:,current) !st Defined at the start to get unmodified value of a(:,:,:,current). + + if(previous == current) then + a(:,:,:,future ) = a(:,:,:,previous) + delta_t*dt_a + a(:,:,:,current) = a(:,:,:,current ) + robert_coeff * (prev_curr_part_raw_filter + a(:,:,:,future ))*raw_filter_coeff + else + a(:,:,:,current) = a(:,:,:,current ) + robert_coeff * (prev_curr_part_raw_filter )*raw_filter_coeff + a(:,:,:,future ) = a(:,:,:,previous) + delta_t * dt_a + a(:,:,:,current) = a(:,:,:,current ) + robert_coeff * a(:,:,:,future)*raw_filter_coeff + endif + + a(:,:,:,future ) = a(:,:,:,future ) + robert_coeff * (prev_curr_part_raw_filter + a(:,:,:,future )) * (raw_filter_coeff-1.0) + + if (q_decrease_only) then + do i=1, size(dt_a,1) + do j=1, size(dt_a,2) + do k=size(dt_a,3)-1, 1, -1 + if (a(1,1,k,future) > a(1,1,k+1,future)) then + a(:,:,k,future) = a(:,:,k+1,future) + endif + end do + end do + end do + endif + + + + !st RAW filter (see e.g. Williams 2011 10.1175/2010MWR3601.1) conserves 3-time-level mean in leap-frog integrations, improving amplitude accuracy of leap-frog scheme from first to third order). + + return + end subroutine leapfrog_3d_real + + + + +end module column_mod \ No newline at end of file diff --git a/src/atmos_column/column_grid.F90 b/src/atmos_column/column_grid.F90 new file mode 100644 index 000000000..587ca9cd3 --- /dev/null +++ b/src/atmos_column/column_grid.F90 @@ -0,0 +1,461 @@ +module column_grid_mod + + +#ifdef INTERNAL_FILE_NML + use mpp_mod, only: input_nml_file +#else + use fms_mod, only: open_namelist_file +#endif + +use fms_mod, only: mpp_pe, mpp_root_pe, error_mesg, FATAL, write_version_number, check_nml_error, stdlog +use mpp_mod, only: mpp_error +use mpp_domains_mod, only: domain1D, mpp_get_compute_domains, mpp_get_domain_components, mpp_get_layout, mpp_global_field +use constants_mod, only: pi +use spec_mpp_mod, only: get_grid_domain, grid_domain, get_spec_domain + +implicit none +private + +character(len=128), parameter :: version = '$Id: column_grid.F90,v 0.1 2018/16/11 HH:MM:SS isca Exp $' +character(len=128), parameter :: tagname = '$Name: isca_201811 $' + +public :: column_grid_init +public :: get_sin_lat, area_weighted_global_mean !, got_cos_lat, getcosm_lat, get_cosm2_lat +public :: get_deg_lat, get_deg_lon, get_grid_boundaries +public :: get_lon_max, get_lat_max!, get_longitude_origin + +integer :: num_lon, num_lat, lat_max +real :: longitude_origin_local +logical :: module_is_initialized = .false. + +real, allocatable, dimension(:) :: deg_lon, deg_lat +real, allocatable, dimension(:) :: sin_lat +real, allocatable, dimension(:) :: cos_lat +real, allocatable, dimension(:) :: cosm_lat +real, allocatable, dimension(:) :: cosm2_lat +real, allocatable, dimension(:) :: wts_lat +real, allocatable, dimension(:) :: sin_hem +real, allocatable, dimension(:) :: wts_hem +real, allocatable, dimension(:) :: lon_boundaries_global +real, allocatable, dimension(:) :: lat_boundaries_global +real :: global_sum_of_wts +real :: sum_wts + +logical :: south_to_north_local + +integer :: is, ie, js, je + + +!! namelist parameters + +real :: lat_value = 0.0 +logical :: global_average = .false. + +namelist / column_grid_nml / lat_value, global_average + +contains + +subroutine column_grid_init(num_lon_in, num_lat_in, longitude_origin, south_to_north) + + integer, intent(in) :: num_lon_in, num_lat_in + real, intent(in), optional :: longitude_origin + logical, intent(in), optional :: south_to_north + + real, parameter :: total_degrees = 360. + real :: del_lat, del_lon + integer :: i, j + integer :: unit, ierr, io + + if(module_is_initialized) return + +#ifdef INTERNAL_FILE_NML + read (input_nml_file, nml=column_grid_nml, iostat=io) + ierr = check_nml_error(io, 'column_grid_nml') +#else + unit = open_namelist_file() + ierr=1 + do while (ierr /= 0) + read(unit, nml=column_grid_nml, iostat=io, end=20) + ierr = check_nml_error (io, 'column_grid_nml') + enddo +20 call close_file (unit) +#endif + + call write_version_number(version, tagname) + if(mpp_pe() == mpp_root_pe()) write (stdlog(), nml=column_grid_nml) + + + if(num_lon_in .eq. 0) then + call error_mesg('column_grid_init','num_lon_in cannot be zero', FATAL) + end if + num_lon = num_lon_in + + if(num_lat_in .eq. 0) then + call error_mesg('column_grid_init','num_lat_in cannot be zero', FATAL) + end if + num_lat = num_lat_in + lat_max = num_lat + + if(present(longitude_origin)) then + longitude_origin_local = longitude_origin + else + longitude_origin_local = 0.0 + end if + + call get_grid_domain(is, ie, js, je) + + allocate(deg_lon(num_lon)) + do i=1, num_lon + deg_lon(i) = 180*longitude_origin_local/pi + (i-1) * total_degrees / float(num_lon) + if(deg_lon(i) .ge. total_degrees) then + deg_lon(i) = deg_lon(i) - total_degrees + endif + if(deg_lon(i) .lt. 0.0) then + deg_lon(i) = deg_lon(i) + total_degrees + endif + end do + + allocate(deg_lat(num_lat)) + allocate (sin_lat(lat_max)) + allocate (cos_lat(lat_max)) + allocate (cosm_lat(lat_max)) + allocate (cosm2_lat(lat_max)) + allocate (wts_lat(lat_max)) + if(global_average) then + if ((num_lat .ne. 1) .or.(num_lon .ne. 1)) then + call error_mesg('column_grid_init', 'cannot set global_average = True with num_lat or num_lon .ne. 1', FATAL) + endif + deg_lat(num_lat) = 180*acos(pi / 4)/pi ! value that yields the fraction needed for insolation at this latitude to be equal to that of the global average (S/4) + sin_lat = sin(pi / 180 * deg_lat ) + cos_lat = cos(pi / 180 * deg_lat ) + wts_lat = 1. ! need to set wts lat such that it tricks interpolator_mod into doing the right thing.... + else + if(num_lat .eq. 1) then + deg_lat(num_lat) = lat_value + sin_lat = sin(pi / 180 * deg_lat ) + cos_lat = cos(pi / 180 * deg_lat ) + wts_lat = 1. + else + allocate (sin_hem(lat_max/2)) + allocate (wts_hem(lat_max/2)) + ! del_lat = 90. / (num_lat) + + ! deg_lat(1) = -90 + del_lat + ! do i = 2, num_lat + ! deg_lat(i) = deg_lat(i-1) + del_lat + ! enddo + if(present(south_to_north)) then + south_to_north_local = south_to_north + else + south_to_north_local = .true. + endif + ! if (.not. south_to_north_local) then + ! deg_lat(:) = - deg_lat(:) + ! endif + + call compute_gaussian(sin_hem, wts_hem, lat_max/2) + + if(south_to_north_local) then + sin_lat(1:lat_max/2) = - sin_hem + else + sin_lat(1:lat_max/2) = sin_hem + end if + + do j=1,lat_max/2 + sin_lat(lat_max+1-j) = - sin_lat(j) + wts_lat(j) = wts_hem(j) + wts_lat(lat_max+1-j) = wts_hem(j) + end do + + cos_lat = sqrt(1-sin_lat*sin_lat) + deg_lat = asin(sin_lat)*180.0/pi + endif + endif + cosm_lat = 1./cos_lat + cosm2_lat = 1./(cos_lat*cos_lat) + + ! this is done in transforms mod when spectral_dynamics is used + allocate( lon_boundaries_global(num_lon+1) ) + allocate( lat_boundaries_global(lat_max+1) ) + lat_boundaries_global(1) = 0.0 + if (num_lat .eq. 1) then + lat_boundaries_global(2) = .5*pi + else + sum_wts = 0. + do j=1,lat_max-1 + sum_wts = sum_wts + wts_lat(j) + lat_boundaries_global(j+1) = asin(sum_wts-1.) + end do + lat_boundaries_global(lat_max+1) = .5*pi + lat_boundaries_global(1) = -0.5*pi + if (.not. south_to_north_local) then + lat_boundaries_global(:) = -lat_boundaries_global(:) + end if + del_lon = 2*pi/num_lon + do i=1,num_lon+1 + lon_boundaries_global(i) = longitude_origin_local + (i-1.5)*del_lon + end do + endif + + global_sum_of_wts = sum(wts_lat) + + module_is_initialized = .true. + + return + +end subroutine column_grid_init + + +subroutine compute_gaussian(sin_hem_lcl, wts_hem_lcl, n_hem_lcl) + !---------------------------------------------------------------------- + ! + ! reference: + ! press, h. william, et. al., numerical recipes (fortran version), + ! cambridge, england: cambridge university press (1990) + ! + !------------------------------------------------------------------------ + + integer, intent (in) :: n_hem_lcl + real, intent (out), dimension(n_hem_lcl) :: sin_hem_lcl, wts_hem_lcl + + real :: converg + integer :: itermax + integer :: i, iter, j, n, nprec + real :: pp, p1, p2, p3, z, z1 + + + ! must use a more relaxed convergence criteria on the + ! workstations than that for the cray T90 + ! fez code is commented out + + !if(kind(converg).eq.8) then + ! converg = 1.0E-15 + !else if(kind(converg).eq.4) then + ! converg = 1.0E-7 + !else + ! call error_mesg('compute_gaussian','dont know what value to use for converg', FATAL) + !end if + + ! The 2 lines of code below will yeild a different result than the fez code + ! when kind(converg)=4. converg is 1.0E-6 instead of 1.0E-7 + ! This should be investigated further, but it's OK for now because it yeilds + ! the same result on the HPCS. -- pjp + nprec = precision(converg) + converg = .1**nprec + + + itermax = 10 + + n=2*n_hem_lcl + do i=1,n_hem_lcl + z = cos(pi*(i - 0.25)/(n + 0.5)) + do iter=1,itermax + p1 = 1.0 + p2 = 0.0 + + do j=1,n + p3 = p2 + p2 = p1 + p1 = ((2.0*j - 1.0)*z*p2 - (j - 1.0)*p3)/j + end do + + pp = n*(z*p1 - p2)/(z*z - 1.0E+00) + z1 = z + z = z1 - p1/pp + if(ABS(z - z1) .LT. converg) go to 10 + end do + call error_mesg('column_grid, compute_gaussian','abscissas failed to converge in itermax iterations', FATAL) + + 10 continue + + sin_hem_lcl (i) = z + wts_hem_lcl (i) = 2.0/((1.0 - z*z)*pp*pp) + + end do + +end subroutine compute_gaussian + + +subroutine get_deg_lon(deg_lon_out) + + real, intent (out), dimension(:) :: deg_lon_out + character(len=8) :: chtmp1, chtmp2 + + if(.not.module_is_initialized) then + call error_mesg('column_grid','module column_grid not initialized', FATAL) + end if + if(size(deg_lon_out,1).ne.num_lon) then + write(chtmp1,'(i8)') size(deg_lon_out,1) + write(chtmp2,'(i8)') num_lon + call error_mesg('column_grid', & + 'size of deg_lon does not equal num_lon. size(deg_lon)='//chtmp1//' num_lon='//chtmp2, FATAL) + end if + + deg_lon_out(:) = deg_lon(:) + + return +end subroutine get_deg_lon + +subroutine get_deg_lat(deg_lat_out) + !----------------------------------------------------------------------- + + real, intent (out), dimension(:) :: deg_lat_out + + if(.not. module_is_initialized) then + call error_mesg('column_grid','module column_grid is not initialized', FATAL) + end if + + if(size(deg_lat_out,1).eq.lat_max) then + deg_lat_out = deg_lat + else !assume grid compute domain + deg_lat_out = deg_lat(js:je) + end if + + return +end subroutine get_deg_lat + +subroutine get_grid_boundaries(lon_boundaries, lat_boundaries,global) + !------------------------------------------------------------------------- + + real, intent(out), dimension(:) :: lon_boundaries, lat_boundaries + logical,intent(in),optional :: global + + logical :: global_tmp + character(len=3) :: chtmp1, chtmp2 + + if(.not.module_is_initialized) then + call error_mesg('get_grid_boundaries','column_grid module is not initialized', FATAL) + end if + + if (present(global)) then + global_tmp = global + else + global_tmp = .false. + endif + + if (.not. global_tmp) then + if(size(lon_boundaries,1) /= ie-is+2) then + write(chtmp1,'(i3)') size(lon_boundaries,1) + write(chtmp2,'(i3)') ie-is+2 + call error_mesg('get_grid_boundaries','size(lon_boundaries) is incorrect. size(lon_boundaries)=' & + & //chtmp1//' Should be'//chtmp2, FATAL) + endif + + if(size(lat_boundaries,1) /= je-js+2) then + write(chtmp1,'(i3)') size(lat_boundaries,1) + write(chtmp2,'(i3)') je-js+2 + call error_mesg('get_grid_boundaries','size(lat_boundaries) is incorrect. size(lat_boundaries)=' & + & //chtmp1//' Should be'//chtmp2, FATAL) + endif + + else !global call + if(size(lon_boundaries,1) /= num_lon+1) then + write(chtmp1,'(i3)') size(lon_boundaries,1) + write(chtmp2,'(i3)') num_lon+1 + call error_mesg('get_grid_boundaries','size(lon_boundaries) is incorrect. size(lon_boundaries)=' & + & //chtmp1//' Should be'//chtmp2, FATAL) + endif + + if(size(lat_boundaries,1) /= lat_max+1) then + write(chtmp1,'(i3)') size(lat_boundaries,1) + write(chtmp2,'(i3)') lat_max+1 + call error_mesg('get_grid_boundaries','size(lat_boundaries) is incorrect. size(lat_boundaries)=' & + & //chtmp1//' Should be'//chtmp2, FATAL) + endif + endif + + if (global_tmp) then + lon_boundaries = lon_boundaries_global + lat_boundaries = lat_boundaries_global + else + lon_boundaries = lon_boundaries_global(is:ie+1) + lat_boundaries = lat_boundaries_global(js:je+1) + endif + return + end subroutine get_grid_boundaries + + subroutine get_sin_lat(sin_lat_out) + !----------------------------------------------------------------------- + + real, intent (out), dimension(:) :: sin_lat_out + + if(.not. module_is_initialized) then + call error_mesg('get_sin_lat','column_grid is not initialized', FATAL) + end if + + if(size(sin_lat_out,1).eq.lat_max) then + sin_lat_out = sin_lat + else !assume grid compute domain + sin_lat_out = sin_lat(js:je) + end if + + return + end subroutine get_sin_lat + + subroutine get_wts_lat(wts_lat_out) + !----------------------------------------------------------------------- + + real, intent (out), dimension(:) :: wts_lat_out + + if(.not. module_is_initialized) then + call error_mesg('get_wts_lat','column_grid is not initialized', FATAL) + end if + + if(size(wts_lat_out,1).eq.lat_max) then + wts_lat_out = wts_lat + else !assume grid compute domain + wts_lat_out = wts_lat(js:je) + end if + + return + end subroutine get_wts_lat + + function area_weighted_global_mean(field) + !------------------------------------------------------------------------- + real :: area_weighted_global_mean + real, intent(in), dimension(:,:) :: field + real, dimension(size(field,2)) :: wts_lat + real, dimension(size(field,1), size(field,2)) :: weighted_field_local + real, dimension(num_lon, lat_max) :: weighted_field_global + integer :: j + + call get_wts_lat(wts_lat) + do j=1,size(field,2) + weighted_field_local(:,j) = wts_lat(j)*field(:,j) + enddo + + call mpp_global_field(grid_domain, weighted_field_local, weighted_field_global) + area_weighted_global_mean = sum(weighted_field_global)/(global_sum_of_wts*num_lon) + + return + end function area_weighted_global_mean + + + subroutine get_lon_max(lon_max_out) + + integer, intent (out) :: lon_max_out + + if(.not.module_is_initialized) then + call error_mesg('get_lon_max','module column_grid not initialized', FATAL) + end if + + lon_max_out = num_lon + + return + end subroutine get_lon_max + + subroutine get_lat_max(lat_max_out) + !------------------------------------------------------------------------- + + integer, intent(out) :: lat_max_out + + if(.not.module_is_initialized) then + call error_mesg('get_lat_max','column_grid module is not initialized', FATAL) + end if + + lat_max_out = lat_max + + return + end subroutine get_lat_max + +end module column_grid_mod diff --git a/src/atmos_column/column_init_cond.F90 b/src/atmos_column/column_init_cond.F90 new file mode 100644 index 000000000..c9f0bc958 --- /dev/null +++ b/src/atmos_column/column_init_cond.F90 @@ -0,0 +1,119 @@ +module column_init_cond_mod + + +#ifdef INTERNAL_FILE_NML +use mpp_mod, only: input_nml_file +#else +use fms_mod, only: open_namelist_file +#endif + +use constants_mod, only: grav, pi +use column_initialize_fields_mod, only: column_initialize_fields +use fms_mod, only: mpp_pe, mpp_root_pe, error_mesg, FATAL, field_size, stdlog, file_exist, & + write_version_number, close_file, check_nml_error, read_data +use mpp_domains_mod, only: mpp_get_global_domain +use mpp_domains_mod, only: mpp_get_global_domain +use spec_mpp_mod, only: grid_domain, get_grid_domain +use vert_coordinate_mod, only: compute_vert_coord +use press_and_geopot_mod, only: press_and_geopot_init, pressure_variables +use tracer_type_mod, only: tracer_type + +implicit none +private + +public :: column_init_cond + +character(len=128), parameter :: version = '$Id: column_init_cond.F90,v 0.1 2018/14/11 HH:MM:SS isca Exp $' +character(len=128), parameter :: tagname = '$Name: isca_201811 $' + +real :: initial_temperature = 264. +real :: surf_geopotential = 0.0 +real :: surface_wind = 5. + +namelist / column_init_cond_nml / initial_temperature, surf_geopotential, surface_wind + +contains + +subroutine column_init_cond(initial_state_option, tracer_attributes, reference_sea_level_press, use_virtual_temperature, & + vert_coord_option, vert_difference_option, scale_heights, surf_res, & + p_press, p_sigma, exponent, pk, bk, ug, vg, tg, psg, & + grid_tracers, surf_geopotential_out)! NTL START HERE, lon_boundaries, lat_boundaries) + +character(len=*), intent(in) :: initial_state_option +type(tracer_type), intent(inout), dimension(:) :: tracer_attributes +real, intent(in) :: reference_sea_level_press +logical, intent(in) :: use_virtual_temperature +character(len=*), intent(in) :: vert_coord_option, vert_difference_option +real, intent(in) :: scale_heights, surf_res, p_press, p_sigma, exponent +!real, intent(in), dimension(:) :: lon_boundaries, lat_boundaries NTL START HERE +real, intent(out), dimension(:) :: pk, bk +real, intent(out), dimension(:,:,:) :: ug, vg, tg +real, intent(out), dimension(:,: ) :: psg +real, intent(out), dimension(:,:,:,:) :: grid_tracers +real, intent(out), dimension(:,: ) :: surf_geopotential_out + +integer :: unit, ierr, io + +!------------------------------------------------------------------------------------------------ + +#ifdef INTERNAL_FILE_NML + read (input_nml_file, nml=column_init_cond_nml, iostat=io) + ierr = check_nml_error(io, 'column_init_cond_nml') +#else + unit = open_namelist_file() + ierr=1 + do while (ierr /= 0) + read(unit, nml=column_init_cond_nml, iostat=io, end=20) + ierr = check_nml_error (io, 'column_init_cond_nml') + enddo +20 call close_file (unit) +#endif +call write_version_number(version, tagname) +if(mpp_pe() == mpp_root_pe()) write (stdlog(), nml=column_init_cond_nml) + + + +call compute_vert_coord(vert_coord_option, scale_heights, surf_res, exponent, p_press, p_sigma, reference_sea_level_press, pk,bk) +surf_geopotential_out = surf_geopotential ! only option to set topography to uniform surface geopotential +call press_and_geopot_init(pk, bk, use_virtual_temperature, vert_difference_option) + +if(initial_state_option == 'default') then + call column_initialize_fields(reference_sea_level_press, initial_temperature, surface_wind, & + surf_geopotential_out, psg, ug, vg, tg) +else +!!!!!!!! NTL: intial condition from file not yet configured !!!!!!!!!! +!else if(initial_state_option == 'input') then +!call ic_from_external_file(triang_trunc, tracer_attributes, vors, divs, ts, ln_ps, ug, & +!vg, tg, psg, vorg, divg, grid_tracers) + call error_mesg('column_init_cond','invalid initial state, can only choose "default"', FATAL) +endif + +call check_vert_coord(size(ug,3), psg) + +return +end subroutine column_init_cond + +subroutine check_vert_coord(num_levels, psg) + integer, intent(in) :: num_levels + real, intent(in), dimension(:,:) :: psg + real, dimension(size(psg,1), size(psg,2), num_levels ) :: p_full, ln_p_full + real, dimension(size(psg,1), size(psg,2), num_levels+1) :: p_half, ln_p_half + integer :: i,j,k + + call pressure_variables(p_half, ln_p_half, p_full, ln_p_full, psg) + do k=1,size(p_full,3) + do j=1,size(p_full,2) + do i=1,size(p_full,1) + if(p_half(i,j,k+1) < p_half(i,j,k)) then + call error_mesg('column_init_cond: check_vert_coord','Pressure levels intersect.',FATAL) + endif + enddo + enddo + enddo + + return + end subroutine check_vert_coord + + + +end module column_init_cond_mod \ No newline at end of file diff --git a/src/atmos_column/column_initialize_fields.F90 b/src/atmos_column/column_initialize_fields.F90 new file mode 100644 index 000000000..e9bb44eba --- /dev/null +++ b/src/atmos_column/column_initialize_fields.F90 @@ -0,0 +1,92 @@ +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +!! !! +!! GNU General Public License !! +!! !! +!! This file is part of the Flexible Modeling System (FMS). !! +!! !! +!! FMS is free software; you can redistribute it and/or modify it !! +!! under the terms of the GNU General Public License as published by !! +!! the Free Software Foundation, either version 3 of the License, or !! +!! (at your option) any later version. !! +!! !! +!! FMS is distributed in the hope that it will be useful, !! +!! but WITHOUT ANY WARRANTY; without even the implied warranty of !! +!! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the !! +!! GNU General Public License for more details. !! +!! !! +!! You should have received a copy of the GNU General Public License !! +!! along with FMS. if not, see: http://www.gnu.org/licenses/gpl.txt !! +!! !! +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +module column_initialize_fields_mod + +use fms_mod, only: mpp_pe, mpp_root_pe, write_version_number + +use column_grid_mod, only: area_weighted_global_mean + +use constants_mod, only: rdgas + +use spec_mpp_mod, only: get_grid_domain + +implicit none +private + +public :: column_initialize_fields + +character(len=128), parameter :: version = '$Id: column_initialize_fields.F90,v 0.1 2018/14/11 HH:MM:SS isca Exp $' +character(len=128), parameter :: tagname = '$Name: isca_201811 $' + +contains + +!------------------------------------------------------------------------------------------------- +subroutine column_initialize_fields(reference_sea_level_press, initial_temperature, surface_wind, & + surf_geopotential, psg, ug, vg, tg) + +real, intent(in) :: reference_sea_level_press +real, intent(in) :: initial_temperature +real, intent(in) :: surface_wind + +real, intent(in), dimension(:,: ) :: surf_geopotential +real, intent(out), dimension(:,: ) :: psg +real, intent(out), dimension(:,:,: ) :: ug, vg, tg + +real, allocatable, dimension(:,:) :: ln_psg + +real :: initial_sea_level_press, global_mean_psg +real :: initial_perturbation = 1.e-7 + +integer :: is, ie, js, je, num_levels + +call write_version_number(version, tagname) + +num_levels = size(ug,3) +call get_grid_domain(is, ie, js, je) +allocate(ln_psg(is:ie, js:je)) + +initial_sea_level_press = reference_sea_level_press + +ug = 0. +vg = 0. +tg = 0. +psg = 0. + +ug(:,:,num_levels) = surface_wind / sqrt(2.) +vg(:,:,num_levels) = surface_wind / sqrt(2.) + +tg = initial_temperature +ln_psg = log(initial_sea_level_press) - surf_geopotential/(rdgas*initial_temperature) +psg = exp(ln_psg) + + +! compute and print mean surface pressure +global_mean_psg = area_weighted_global_mean(psg) +if(mpp_pe() == mpp_root_pe()) then + print '("mean surface pressure=",f9.4," mb")',.01*global_mean_psg +endif + +return +end subroutine column_initialize_fields +!================================================================================ + +end module column_initialize_fields_mod diff --git a/src/atmos_param/betts_miller/betts_miller.f90 b/src/atmos_param/betts_miller/betts_miller.f90 index 0e03b53cb..bc0caab06 100644 --- a/src/atmos_param/betts_miller/betts_miller.f90 +++ b/src/atmos_param/betts_miller/betts_miller.f90 @@ -19,7 +19,7 @@ module betts_miller_mod !--------------------------------------------------------------------- ! ---- public interfaces ---- - public betts_miller, betts_miller_init + public betts_miller, betts_miller_init, lcltabl !----------------------------------------------------------------------- ! ---- version number ---- @@ -101,7 +101,7 @@ module betts_miller_mod subroutine betts_miller (dt, tin, qin, pfull, phalf, coldT, & rain, snow, tdel, qdel, q_ref, bmflag, & klzbs, cape, cin, t_ref,invtau_bm_t,invtau_bm_q, & - capeflag, mask, conv) + capeflag, klcls, mask, conv) !----------------------------------------------------------------------- ! @@ -142,9 +142,9 @@ subroutine betts_miller (dt, tin, qin, pfull, phalf, coldT, & real , intent(in) , dimension(:,:,:) :: tin, qin, pfull, phalf real , intent(in) :: dt logical , intent(in) , dimension(:,:):: coldT - real , intent(out), dimension(:,:) :: rain,snow, klzbs, cape, & + real , intent(out), dimension(:,:) :: rain,snow, cape, & cin, invtau_bm_t, invtau_bm_q, capeflag - integer, intent(out), dimension(:,:) :: bmflag + integer, intent(out), dimension(:,:) :: bmflag, klzbs, klcls real , intent(out), dimension(:,:,:) :: tdel, qdel, q_ref, t_ref real , intent(in) , dimension(:,:,:), optional :: mask logical, intent(in) , dimension(:,:,:), optional :: conv @@ -162,8 +162,8 @@ subroutine betts_miller (dt, tin, qin, pfull, phalf, coldT, & real :: & cape1, cin1, tot, deltak, deltaq, qrefint, deltaqfrac, deltaqfrac2, & - ptopfrac, es, capeflag1, plzb, plcl, cape2, small -integer i, j, k, ix, jx, kx, klzb, ktop, klzb2 + ptopfrac, es, capeflag1, plzb, plcl, small + integer i, j, k, ix, jx, kx, klzb, ktop, klcl !----------------------------------------------------------------------- ! computation of precipitation by betts-miller scheme !----------------------------------------------------------------------- @@ -197,13 +197,14 @@ subroutine betts_miller (dt, tin, qin, pfull, phalf, coldT, & call capecalcnew( kx, pfull(i,j,:), phalf(i,j,:),& cp_air, rdgas, rvgas, hlv, kappa, tin(i,j,:), & rin(i,j,:), avgbl, cape1, cin1, tpc, & - rpc, klzb) + rpc, klzb, klcl) ! set values for storage capeflag(i,j) = capeflag1 cape(i,j) = cape1 cin(i,j) = cin1 klzbs(i,j) = klzb + klcls(i,j) = klcl if(cape1.gt.0.) then ! if((tot.gt.0.).and.(cape1.gt.0.)) then bmflag(i,j) = 1 @@ -443,7 +444,7 @@ end subroutine betts_miller !all new cape calculation. subroutine capecalcnew(kx,p,phalf,cp_air,rdgas,rvgas,hlv,kappa,tin,rin,& - avgbl,cape,cin,tp,rp,klzb) + avgbl,cape,cin,tp,rp,klzb,klcl) ! ! Input: @@ -471,6 +472,7 @@ subroutine capecalcnew(kx,p,phalf,cp_air,rdgas,rvgas,hlv,kappa,tin,rin,& ! where no adjustment, and set to the saturation humidity at ! the parcel temperature below the LCL) ! klzb Level of zero buoyancy +! klcl Lifting condensation level ! ! Algorithm: ! Start with surface parcel. @@ -485,11 +487,11 @@ subroutine capecalcnew(kx,p,phalf,cp_air,rdgas,rvgas,hlv,kappa,tin,rin,& logical, intent(in) :: avgbl real, intent(in), dimension(:) :: p, phalf, tin, rin real, intent(in) :: rdgas, rvgas, hlv, kappa, cp_air - integer, intent(out) :: klzb + integer, intent(out) :: klzb, klcl real, intent(out), dimension(:) :: tp, rp real, intent(out) :: cape, cin - integer :: k, klcl, klfc, klcl2 + integer :: k, klfc, klcl2 ! klcl logical :: nocape real, dimension(kx) :: theta real :: t0, r0, es, rs, theta0, pstar, value, tlcl, & diff --git a/src/atmos_param/cloud_simple/cloud_cover_diags.F90 b/src/atmos_param/cloud_simple/cloud_cover_diags.F90 new file mode 100644 index 000000000..aea5b337d --- /dev/null +++ b/src/atmos_param/cloud_simple/cloud_cover_diags.F90 @@ -0,0 +1,379 @@ +module cloud_cover_diags_mod + +#ifdef INTERNAL_FILE_NML + use mpp_mod, only: input_nml_file +#else + use fms_mod, only: open_namelist_file, close_file +#endif + + use fms_mod, only: stdlog, FATAL, WARNING, NOTE, error_mesg, & + uppercase, check_nml_error + use time_manager_mod, only: time_type + use diag_manager_mod, only: register_diag_field, send_data + + implicit none + + character(len=14), parameter :: mod_name = "cloud_cover" + + ! Overlap assumptions include 'maximum-random', 'maximum', and 'random', + ! and the default is 'maximum-random'. + character(len=32) :: overlap_assumption = 'maximum-random' + + real :: cf_min = 0.0 ! The clear-sky threshold + + real :: mid_cld_bottom = 7.0e4 ! Bottom (Top) pressure of middle (low) clouds + real :: high_cld_bottom = 4.0e4 ! Bottom (Top) pressure of high (middle) clouds + + ! ----- outputs for cloud amount diagnostics ----- ! + integer :: id_tot_cld_amt, id_high_cld_amt, id_mid_cld_amt, id_low_cld_amt + + namelist /cloud_cover_diag_nml/ & + overlap_assumption, cf_min, mid_cld_bottom, high_cld_bottom + + contains + + ! =================================================== + ! cloud cover diags init + ! =================================================== + subroutine cloud_cover_diags_init(axes, Time) + type(time_type), intent(in) :: Time + integer, intent(in), dimension(4) :: axes + integer :: io, ierr, nml_unit, stdlog_unit + +#ifdef INTERNAL_FILE_NML + read(input_nml_file, nml=cloud_cover_diag_nml, iostat=io) + ierr = check_nml_error(io, 'cloud_cover_diag_nml') +#else + if (file_exist('input.nml')) then + nml_unit = open_namelist_file() + ierr = 1 + do while (ierr /= 0) + read(nml_unit, nml=cloud_cover_diag_nml, iostat=io, end=10) + ierr = check_nml_error(io, 'cloud_cover_diag_nml') + enddo +10 call close_file(nml_unit) + endif +#endif + stdlog_unit = stdlog() + write(stdlog_unit, cloud_cover_diag_nml) + + call error_mesg(mod_name, 'The cloud overlap assumption is '// & + uppercase(trim(overlap_assumption)), NOTE) + + id_tot_cld_amt = register_diag_field (mod_name, 'tot_cld_amt', axes(1:2), Time, & + 'total cloud amount (%)', 'percent') + id_high_cld_amt = register_diag_field (mod_name, 'high_cld_amt', axes(1:2), Time, & + 'high cloud amount (%)', 'percent') + id_mid_cld_amt = register_diag_field (mod_name, 'mid_cld_amt', axes(1:2), Time, & + 'mid cloud amount (%)', 'percent') + id_low_cld_amt = register_diag_field (mod_name, 'low_cld_amt', axes(1:2), Time, & + 'low cloud amount (%)', 'percent') + + end subroutine cloud_cover_diags_init + + ! =================================================== + ! cloud cover diags init + ! =================================================== + subroutine cloud_cover_diags(cf, p_full, p_half, Time) + real, intent(in), dimension(:,:,:) :: cf, p_full, p_half + type(time_type), intent(in) :: Time + character(len=32) :: overlap_str = '' + + overlap_str = uppercase(trim(overlap_assumption)) + + if (overlap_str == 'MAXIMUM-RANDOM') then + call diag_cldamt_maxrnd_overlap(cf, p_full, p_half, Time) + + else if (overlap_str == 'MAXIMUM') then + call diag_cldamt_max_overlap(cf, p_full, Time) + + else if (overlap_str == 'RANDOM') then + call diag_cldamt_random_overlap(cf, p_full, Time) + + else + call error_mesg(mod_name, '"'//trim(overlap_assumption)//'"'// & + ' is not a valid cloud overlap assumption.', FATAL) + end if + + end subroutine cloud_cover_diags + + subroutine diag_cldamt_maxrnd_overlap(cf, pmid, pint, Time) + ! Original codes are from CESM, refer to: + ! https://github.com/E3SM-Project/E3SM/blob/master/components/cam/src/physics/cam/cloud_cover_diags.F90 + + !!! pmid is pfull, and pint is phalf + real, intent(in), dimension(:,:,:) :: cf, pmid, pint + type(time_type), intent(in) :: Time + + !---------------------------Local workspace----------------------------- + ! Total, low, middle and high random overlap cloud cover + real, dimension(size(cf,1), size(cf,2)) :: tot_ca, hgh_ca, mid_ca, low_ca + + integer :: i, j, k ! lat, lon, level indices + integer, dimension(size(cf,1), size(cf,2)) :: irgn ! Max-overlap region index + integer :: max_nmxrgn ! maximum value of nmxrgn over columns + integer :: ityp ! Type counter + real, dimension(size(cf,1), size(cf,2)) :: clrsky ! Max-random clear sky fraction + real, dimension(size(cf,1), size(cf,2)) :: clrskymax ! Maximum overlap clear sky fraction + integer, dimension(size(cf,1), size(cf,2)) :: nmxrgn + real, dimension(size(pint,1), size(pint,2), size(pint,3)) :: pmxrgn + + !------------------------------Cloud Range Paramters------------------------------- + real :: plowmax, plowmin ! Max/min prs for low cloud cover range + real :: pmedmax, pmedmin ! Max/min prs for mid cloud cover range + real :: phghmax, phghmin ! Max/min prs for hgh cloud cover range + + real, dimension(4) :: ptypmin + real, dimension(4) :: ptypmax + + plowmax = 1.2e5 + plowmin = mid_cld_bottom + + pmedmax = mid_cld_bottom + pmedmin = high_cld_bottom + + phghmax = high_cld_bottom + phghmin = 5.0e3 + + ptypmin = (/ phghmin, plowmin, pmedmin, phghmin /) + ptypmax = (/ plowmax, plowmax, pmedmax, phghmax /) + + ! call the overlap subroutine to obtain the nmxrgn and pmxrgn + call cldovrlap(pint, cf, nmxrgn, pmxrgn) + + ! Initialize region number + max_nmxrgn = -1 + do i = 1,size(cf,1) + do j = 1,size(cf,2) + max_nmxrgn = max(max_nmxrgn, nmxrgn(i,j)) + end do + end do + + do ityp = 1,4 + irgn = 1 + do k = 1,max_nmxrgn-1 + do i = 1,size(cf,1) + do j = 1,size(cf,2) + if (pmxrgn(i,j,irgn(i,j)) < ptypmin(ityp) .and. irgn(i,j) < nmxrgn(i,j)) then + irgn(i,j) = irgn(i,j) + 1 + end if + end do + end do + end do + ! + ! Compute cloud amount by estimating clear-sky amounts + ! + clrsky = 1.0 + clrskymax = 1.0 + + do i = 1,size(cf,1) + do j = 1,size(cf,2) + do k = 1,size(cf,3) + if (pmid(i,j,k) >= ptypmin(ityp) .and. pmid(i,j,k) <= ptypmax(ityp)) then + if (pmxrgn(i,j,irgn(i,j)) < pmid(i,j,k) .and. irgn(i,j) < nmxrgn(i,j)) then + irgn(i,j) = irgn(i,j) + 1 + clrsky(i,j) = clrsky(i,j) * clrskymax(i,j) + clrskymax(i,j) = 1.0 + endif + clrskymax(i,j) = min(clrskymax(i,j), 1.0-cf(i,j,k)) + endif + end do + end do + end do + + if (ityp == 1) tot_ca = 1.0 - (clrsky * clrskymax) + if (ityp == 2) low_ca = 1.0 - (clrsky * clrskymax) + if (ityp == 3) mid_ca = 1.0 - (clrsky * clrskymax) + if (ityp == 4) hgh_ca = 1.0 - (clrsky * clrskymax) + end do + + ! Diagnostics output + call output_cldamt(tot_ca, hgh_ca, mid_ca, low_ca, Time) + + end subroutine diag_cldamt_maxrnd_overlap + + subroutine cldovrlap(pint, cf, nmxrgn, pmxrgn) + ! The original codes are from CAM. + ! Please refer to: + ! https://github.com/E3SM-Project/E3SM/blob/master/components/cam/src/physics/cam/pkg_cldoptics.F90#L136 + + !----------------------------------------------------------------------- + ! Purpose: + ! Partitions each column into regions with clouds in neighboring layers. + ! This information is used to implement maximum overlap in these regions + ! with random overlap between them. + ! On output: + ! nmxrgn contains the number of regions in each column + ! pmxrgn contains the interface pressures for the lower boundaries of each region! + ! + ! Author: W. Collins + !----------------------------------------------------------------------- + + ! Input arguments + real, intent(in), dimension(:,:,:) :: pint ! Interface pressure + real, intent(in), dimension(:,:,:) :: cf ! Fractional cloud cover + + ! Output arguments + ! Number of maximally overlapped regions + integer, intent(out), dimension(size(cf,1), size(cf,2)) :: nmxrgn + real, intent(out), dimension(size(pint,1), size(pint,2), size(pint,3)) :: pmxrgn + + !---------------------------Local variables----------------------------- + integer :: i, j ! Lat/Longitude index + integer :: k ! Level index + integer :: n ! Max-overlap region counter + + real, dimension(size(pint,1), size(pint,2), size(pint,3)) :: pnm ! Interface pressure + logical :: cld_found ! Flag for detection of cloud + logical, dimension(size(cf,3)) :: cld_layer ! Flag for cloud in layer + integer :: pver, pverp + + pver = size(cf,3) + pverp = pver + 1 + + do i = 1,size(cf,1) + do j = 1,size(cf,2) + cld_found = .false. + ! True if cloud fraction greater than cf_min + cld_layer(:) = cf(i,j,:) > cf_min ! 0.0 + pmxrgn(i,j,:) = 0.0 + pnm(i,j,:) = pint(i,j,:) + n = 1 + do k = 1, pver + if (cld_layer(k) .and. .not. cld_found) then + cld_found = .true. + else if (.not. cld_layer(k) .and. cld_found) then + cld_found = .false. + if (count(cld_layer(k:pver)) == 0) then + exit + endif + pmxrgn(i,j,n) = pnm(i,j,k) + n = n + 1 + endif + end do + pmxrgn(i,j,n) = pnm(i,j,pverp) + nmxrgn(i,j) = n + end do + end do + end subroutine cldovrlap + + subroutine diag_cldamt_max_overlap(cf, p_full, Time) + real, intent(in), dimension(:,:,:) :: cf, p_full + type(time_type), intent(in) :: Time + real, dimension(size(cf,1), size(cf,2)) :: tot_ca, hgh_ca, mid_ca, low_ca + integer :: i, j, ks, ke + logical, dimension(size(cf,3)) :: ind_mid + + tot_ca = 1.0 + hgh_ca = 1.0 + mid_ca = 1.0 + low_ca = 1.0 + + ! total cf amount + tot_ca = maxval(cf, 3) + + do i = 1,size(cf,1) + do j = 1,size(cf,2) + ! low cloud amount + ks = minloc(p_full(i,j,:), 1, mask=p_full(i,j,:)>mid_cld_bottom) + ke = maxloc(p_full(i,j,:), 1, mask=p_full(i,j,:)>mid_cld_bottom) + low_ca(i,j) = maxval(cf(i,j,ks:ke), 1) + + ! middle cloud amount + ind_mid = high_cld_bottom<=p_full(i,j,:) .and. p_full(i,j,:)<=mid_cld_bottom + ks = minloc(p_full(i,j,:), 1, mask=ind_mid) + ke = maxloc(p_full(i,j,:), 1, mask=ind_mid) + mid_ca(i,j) = maxval(cf(i,j,ks:ke), 1) + + ! high cloud amount + ks = minloc(p_full(i,j,:), 1, mask=p_full(i,j,:)mid_cld_bottom) + ke = maxloc(p_full(i,j,:), 1, mask=p_full(i,j,:)>mid_cld_bottom) + call random_overlap_single_column(cf(i,j,ks:ke), low_ca(i,j)) + + ! middle cloud amount + ind_mid = high_cld_bottom<=p_full(i,j,:) .and. p_full(i,j,:)<=mid_cld_bottom + ks = minloc(p_full(i,j,:), 1, mask=ind_mid) + ke = maxloc(p_full(i,j,:), 1, mask=ind_mid) + call random_overlap_single_column(cf(i,j,ks:ke), mid_ca(i,j)) + + ! high cloud amount + ks = minloc(p_full(i,j,:), 1, mask=p_full(i,j,:) 0) then + used = send_data (id_tot_cld_amt, tot_ca*1.0e2, Time) + endif + if (id_high_cld_amt > 0) then + used = send_data (id_high_cld_amt, hgh_ca*1.0e2, Time) + endif + if (id_mid_cld_amt > 0) then + used = send_data (id_mid_cld_amt, mid_ca*1.0e2, Time) + endif + if (id_low_cld_amt > 0) then + used = send_data (id_low_cld_amt, low_ca*1.0e2, Time) + endif + end subroutine output_cldamt + + ! =================================================== + ! cloud cover diags end + ! =================================================== + subroutine cloud_cover_diags_end() + + end subroutine cloud_cover_diags_end + +end module cloud_cover_diags_mod diff --git a/src/atmos_param/cloud_simple/cloud_simple.F90 b/src/atmos_param/cloud_simple/cloud_simple.F90 new file mode 100644 index 000000000..2e65dfaa6 --- /dev/null +++ b/src/atmos_param/cloud_simple/cloud_simple.F90 @@ -0,0 +1,212 @@ +module cloud_simple_mod + +#ifdef INTERNAL_FILE_NML + use mpp_mod, only: input_nml_file +#else + use fms_mod, only: open_namelist_file, close_file +#endif + + use fms_mod, only: stdlog, FATAL, WARNING, NOTE, error_mesg, & + uppercase, check_nml_error + use time_manager_mod, only: time_type + use sat_vapor_pres_mod, only: compute_qs, lookup_es + use diag_manager_mod, only: register_diag_field, send_data + use constants_mod, only: CP_AIR, GRAV, RDGAS, RVGAS, HLV, KAPPA, RADIUS, TFREEZE + use lcl_mod, only: lcl + use large_scale_cloud_mod, only: large_scale_cloud_init, large_scale_cloud_diag, & + large_scale_cloud_end + use marine_strat_cloud_mod, only: marine_strat_cloud_init, marine_strat_cloud_diag, & + marine_strat_cloud_end + use cloud_cover_diags_mod, only: cloud_cover_diags_init, cloud_cover_diags, & + cloud_cover_diags_end + + implicit none + + character(len=14), parameter :: mod_name_cld = "cloud_simple" + + logical :: do_init = .true. ! Check if init needs to be run + logical :: do_qcl_with_temp = .true. + logical :: do_cloud_cover_diags = .true. + logical :: do_add_stratocumulus = .false. + + ! Parameters to control the liquid cloud fraction + real :: T_max = -5 ! Units in Celcius + real :: T_min = -40 ! Units in Celcius + + ! For effective cloud droplet radius + real :: reff_liq = 14.0 ! micron + real :: reff_ice = 25.0 + + ! For in-cloud liquid water + real :: qcl_val = 0.2 ! g/kg + + ! ----- outputs for baisc cloud diagnostics ----- ! + integer :: id_cf, id_reff_rad, id_frac_liq, id_qcl_rad, id_rh_in_cf + + namelist /cloud_simple_nml/ & + T_min, T_max, reff_liq, reff_ice, & + qcl_val, do_qcl_with_temp, & + do_add_stratocumulus, do_cloud_cover_diags + + contains + + subroutine cloud_simple_init (axes, Time) + type(time_type), intent(in) :: Time + integer, intent(in), dimension(4) :: axes + integer :: io, ierr, nml_unit, stdlog_unit + +#ifdef INTERNAL_FILE_NML + read(input_nml_file, nml=cloud_simple_nml, iostat=io) + ierr = check_nml_error(io, 'cloud_simple_nml') +#else + if (file_exist('input.nml')) then + nml_unit = open_namelist_file() + ierr = 1 + do while (ierr /= 0) + read(nml_unit, nml=cloud_simple_nml, iostat=io, end=10) + ierr = check_nml_error(io, 'cloud_simple_nml') + enddo +10 call close_file(nml_unit) + endif +#endif + stdlog_unit = stdlog() + write(stdlog_unit, cloud_simple_nml) + + call error_mesg(mod_name_cld, 'Using SimCloud cloud scheme', NOTE) + + !register diagnostics + id_cf = register_diag_field (mod_name_cld, 'cf', axes(1:3), Time, & + 'Cloud fraction for the simple cloud scheme', 'unitless: values 0-1') + id_frac_liq = register_diag_field (mod_name_cld, 'frac_liq', axes(1:3), Time, & + 'Liquid cloud fraction (liquid, mixed-ice phase, ice)', 'unitless: values 0-1') + id_reff_rad = register_diag_field (mod_name_cld, 'reff_rad', axes(1:3), Time, & + 'Effective cloud particle radius', 'microns') + id_qcl_rad = register_diag_field (mod_name_cld, 'qcl_rad', axes(1:3), Time, & + 'Specific humidity of cloud liquid', 'kg/kg') + id_rh_in_cf = register_diag_field (mod_name_cld, 'rh_in_cf', axes(1:3), Time, & + 'RH as a percent', '%') + + call large_scale_cloud_init(axes, Time) + + if (do_add_stratocumulus) call marine_strat_cloud_init(axes, Time) + + if (do_cloud_cover_diags) call cloud_cover_diags_init(axes, Time) + + do_init = .false. !initialisation completed + + end subroutine cloud_simple_init + + ! ====================== Main Cloud Subroutine ====================== ! + subroutine cloud_simple(p_half, p_full, Time, temp, q_hum, z_full, & + wg_full, psg, temp_2m, q_2m, rh_2m, klcls, ocean, & + cf, reff_rad, qcl_rad) ! outs + real, intent(in), dimension(:,:,:) :: temp, q_hum, p_full, p_half, z_full, wg_full + real, intent(in), dimension(:,:) :: psg, temp_2m, q_2m, rh_2m + integer, intent(in), dimension(:,:) :: klcls + logical, intent(in), dimension(:,:) :: ocean + type(time_type), intent(in) :: Time + real, intent(out), dimension(:,:,:) :: cf, reff_rad, qcl_rad + real, dimension(size(temp,1), size(temp,2), size(temp,3)) :: qs, frac_liq, rh_in_cf + + !check initiation has been done - ie read in parameters + if (do_init) call error_mesg ('cloud_simple', & + 'cloud_simple_init has not been called.', FATAL) + + ! Get the saturated specific humidity TOTAL (ie ice and vap) ***double check maths! + call compute_qs(temp, p_full, qs) + rh_in_cf = q_hum / qs + + call calc_liq_frac(temp, frac_liq) + + call calc_reff(frac_liq, reff_rad) + + call large_scale_cloud_diag(p_full, psg, rh_in_cf, q_hum, qs, qcl_rad, wg_full, cf, Time) + + if (do_add_stratocumulus) then + call marine_strat_cloud_diag(temp, p_full, p_half, z_full, rh_in_cf, q_hum, & + temp_2m, q_2m, rh_2m, psg, wg_full, klcls, cf, Time, ocean) + end if + + call calc_qcl_rad(p_full, temp, cf, qcl_rad) + + if (do_cloud_cover_diags) call cloud_cover_diags(cf, p_full, p_half, Time) + + call output_cloud_diags(cf, reff_rad, frac_liq, qcl_rad, rh_in_cf, Time) + + end subroutine cloud_simple + + subroutine calc_liq_frac(temp, frac_liq) + ! All liquid if above T_max and all ice below T_min, + ! linearly interpolate between T_min and T_max + real, intent(in), dimension(:,:,:) :: temp + real, intent(out), dimension(:,:,:) :: frac_liq + + where (temp > TFREEZE + T_max) + frac_liq = 1.0 + elsewhere (temp < TFREEZE + T_min) + frac_liq = 0.0 + elsewhere + frac_liq = (temp - TFREEZE - T_min) / (T_max - T_min) + end where + + end subroutine calc_liq_frac + + subroutine calc_reff(frac_liq, reff_rad) + real, intent(in), dimension(:,:,:) :: frac_liq + real, intent(out), dimension(:,:,:) :: reff_rad + + reff_rad = reff_liq * frac_liq + reff_ice * (1.0 - frac_liq) + + end subroutine calc_reff + + subroutine calc_qcl_rad(p_full, temp, cf, qcl_rad) + ! calculate cloud water content + real, intent(in), dimension(:,:,:) :: p_full, cf, temp + real, intent(out), dimension(:,:,:) :: qcl_rad + real, dimension(size(temp,1), size(temp,2), size(temp,3)) :: in_cloud_qcl + + if (do_qcl_with_temp) then + in_cloud_qcl = qcl_val * (temp-220.0) / (280.0-220.0) + in_cloud_qcl = MAX(3.0e-4, MIN(qcl_val, in_cloud_qcl)) / 1.0e3 ! convert to kg/kg + else + ! in_cloud_qcl as a function of height + in_cloud_qcl = 3.0e-4 + (1.0-3.0e-4) * (p_full-2.0e4) / 8.0e4 + in_cloud_qcl = MAX(0.0, in_cloud_qcl/1.0e3) ! convert to kg/kg + end if + + qcl_rad = cf * in_cloud_qcl + + end subroutine calc_qcl_rad + + subroutine output_cloud_diags(cf, reff_rad, frac_liq, qcl_rad, rh_in_cf, Time) + real, intent(in), dimension(:,:,:) :: cf, reff_rad, frac_liq, qcl_rad, rh_in_cf + type(time_type) , intent(in) :: Time + logical :: used + + if (id_cf > 0) then + used = send_data (id_cf, cf, Time) + endif + if (id_reff_rad > 0) then + used = send_data (id_reff_rad, reff_rad, Time) + endif + if (id_frac_liq > 0) then + used = send_data (id_frac_liq, frac_liq, Time) + endif + if (id_qcl_rad > 0) then + used = send_data (id_qcl_rad, qcl_rad, Time) + endif + if (id_rh_in_cf > 0) then + used = send_data (id_rh_in_cf, rh_in_cf*100., Time) + endif + + end subroutine output_cloud_diags + + subroutine cloud_simple_end() + + call large_scale_cloud_end() + if (do_add_stratocumulus) call marine_strat_cloud_end() + if (do_cloud_cover_diags) call cloud_cover_diags_end() + + end subroutine cloud_simple_end + +end module cloud_simple_mod diff --git a/src/atmos_param/cloud_simple/cloud_spookie.F90 b/src/atmos_param/cloud_simple/cloud_spookie.F90 new file mode 100644 index 000000000..7dcf50738 --- /dev/null +++ b/src/atmos_param/cloud_simple/cloud_spookie.F90 @@ -0,0 +1,391 @@ +module cloud_spookie_mod + + use fms_mod, only: stdlog, FATAL, WARNING, NOTE, error_mesg, & + open_namelist_file, close_file, open_file, & + check_nml_error, mpp_pe + use time_manager_mod, only: time_type + use sat_vapor_pres_mod, only: compute_qs + use constants_mod, only: KELVIN + + use diag_manager_mod, only: register_diag_field, send_data + + implicit none + + character(len=128) :: version='$Id: cloud_spookie.F90,v 1.0 2021/05/11$' + character(len=128) :: tag='Simple cloud scheme - SPOOKIE Protocol' + + logical :: do_init = .true. ! update to false after init has been run + + real :: cca_lower_limit = 0.0 ! simple convective cloud fraction min + ! the default is zero. Not being used but + ! could be adapted in future. + + ! There are two testing scenarios developed for SPOOKIE-2 project. Which + ! is why this code was created. The first was implemented and found not to + ! work very well in other models. It has been implemented her for curiousity + ! testing but not tested very much yet. The second protocol is what was used + ! for the SPOOKIE-2 runs and has been more widely tested. For this reason, + ! the default spookie_protocol is 2. The 1st protocol could be removed in + ! time (once the SPOOKIE-2 project is complete, but is left in the code for + ! now in case it is needed. + + integer :: spookie_protocol = 2 ! default is 2 + + ! Critical RH (fraction) values - SPOOKIE-2 protocol version 1 + real :: rhc_sfc = 1.0 + real :: rhc_base = 0.7 + real :: rhc_top = 0.2 ! In the protocol this was 20 % and in + ! implementation it was 30%. To check in + ! next round of validation. + + ! Critical RH (fraction) values - SPOOKIE-2 protocol version 2 + ! initial values for RH. Updated in calc_rh_min_max + real :: rh_min_sfc = 1.0 + real :: rh_min_base = 0.8 + real :: rh_min_top = 0.9 + + real :: rh_max_sfc = 1.0 + real :: rh_max_base = 1.0 + real :: rh_max_top = 1.0 + + ! Pressure (Pa) at cloud bottom and top (very approx) + real :: p_base = 70000. + real :: p_top = 20000. + + namelist /cloud_spookie_nml/ cca_lower_limit, spookie_protocol, & + rhc_sfc, rhc_base, rhc_top, & + rh_min_top, rh_min_sfc, rh_min_base, & + rh_max_top, rh_max_sfc, rh_max_base + + integer :: id_cf, id_reff_rad, id_frac_liq, id_qcl_rad, id_rh_in_cf, & + id_simple_rhcrit, id_rh_min + + character(len=14), parameter :: mod_name_cld = "cloud_simple" + + contains + + !----------------------------------------------- + + + subroutine cloud_spookie_init (axes, Time) + + type(time_type), intent(in) :: Time + integer, intent(in), dimension(4) :: axes + + integer :: io, ierr, unit + + unit = open_file (file='input.nml', action='read') + ierr=1 + do while (ierr /= 0) + read (unit, nml=cloud_spookie_nml, iostat=io, end=10) + ierr = check_nml_error (io, 'cloud_spookie_nml') + enddo + 10 call close_file (unit) + + unit = open_file (file='logfile.out', action='append') + if ( mpp_pe() == 0 ) then + write (unit,'(/,80("="),/(a))') trim(version), trim(tag) + write (unit,nml=cloud_spookie_nml) + endif + call close_file(unit) + + call error_mesg(mod_name_cld, 'Using SPOOKIE protocol cloud scheme', NOTE) + + !register diagnostics + id_cf = & + register_diag_field ( mod_name_cld, 'cf', axes(1:3), Time, & + 'Cloud fraction for the simple cloud scheme', 'unitless: values 0-1') + + id_frac_liq = & + register_diag_field ( mod_name_cld, 'frac_liq', axes(1:3), Time, & + 'Liquid cloud fraction (liquid, mixed-ice phase, ice)', & + 'unitless: values 0-1') + + id_reff_rad = & + register_diag_field ( mod_name_cld, 'reff_rad', axes(1:3), Time, & + 'Effective cloud particle radius', & + 'microns') + + id_qcl_rad = & + register_diag_field ( mod_name_cld, 'qcl_rad', axes(1:3), Time, & + 'Specific humidity of cloud liquid', & + 'kg/kg') + + ! rh_in_cf is an output diagnostic only for debugging + + id_rh_in_cf = & + register_diag_field ( mod_name_cld, 'rh_in_cf', axes(1:3), Time, & + 'RH as a percent', & + '%') + + id_simple_rhcrit = & + register_diag_field ( mod_name_cld, 'simple_rhcrit', axes(1:3), Time, & + 'RH as a percent for spookie protocol 1', & + '%') + + id_rh_min = & + register_diag_field ( mod_name_cld, 'rh_min', axes(1:3), Time, & + 'RH as a percent for spookie protocol 2', & + '%') + + + do_init = .false. !initialisation completed + + end subroutine cloud_spookie_init + + !----------------------------------------------- + + subroutine cloud_spookie(p_half, p_full, Time, & + temp, q_hum, & + ! outs + cf, cca, reff_rad, qcl_rad) + + real , intent(in), dimension(:,:,:) :: temp, q_hum, p_full, p_half + type(time_type) , intent(in) :: Time + + real , intent(inout), dimension(:,:,:) :: cf, cca, reff_rad, qcl_rad + + real, dimension(size(temp,1), size(temp, 2), size(temp, 3)) :: qs, frac_liq + real, dimension(size(temp,1), size(temp, 2), size(temp, 3)) :: rh_in_cf + real, dimension(size(temp,1), size(temp, 2), size(temp, 3)) :: simple_rhcrit + real, dimension(size(temp,1), size(temp, 2), size(temp, 3)) :: rh_min,rh_max + + integer :: i, j, k, k_surf + + !check initiation has been done + if (do_init) call error_mesg ('cloud_spookie', & + 'cloud_spookie_init has not been called.', FATAL) + + ! Get the saturated specific humidity with respect to water and ice + ! this is set by the namelist variable sat_vapor_pres_nml + call compute_qs(temp, p_full, qs) + + k_surf = size(temp, 3) !set the location of the lowest model level + + + ! For future revisions, consider rewriting to remove the loops. + do k=1, size(temp, 3) + do j=1, size(temp, 2) + do i=1, size(temp, 1) + + ! calculate the liquid fraction + call calc_liq_frac(temp(i,j,k), frac_liq(i,j,k)) + + ! calculate the effective radius + call calc_reff(frac_liq(i,j,k), reff_rad(i,j,k)) + + if (spookie_protocol .eq. 1) then + ! calculate the critical RH + call calc_rhcrit(p_full(i,j,k), p_full(i,j,k_surf), & + simple_rhcrit(i,j,k)) + else + ! calculate the min and max RH + call calc_rh_min_max(p_full(i,j,k), p_full(i,j,k_surf), & + rh_min(i,j,k), rh_max(i,j,k)) + endif + + ! calculate the cloud fraction + call calc_cf(q_hum(i,j,k), qs(i,j,k), cf(i,j,k), cca(i,j,k), & + rh_in_cf(i,j,k), simple_rhcrit = simple_rhcrit(i,j,k), & + rh_min = rh_min(i,j,k), rh_max = rh_max(i,j,k) ) + + ! calculate the specific humidity of cloud liquid + call calc_mixing_ratio(p_full(i,j,k), cf(i,j,k), temp(i,j,k), & + qcl_rad(i,j,k) ) + end do + end do + end do + + !save some diagnotics + call output_cloud_diags(cf, reff_rad, frac_liq, qcl_rad, rh_in_cf, & + simple_rhcrit, rh_min, Time ) + + end subroutine cloud_spookie + + subroutine calc_liq_frac(temp, frac_liq) + + real, intent(in) :: temp + real, intent(out) :: frac_liq + + if (temp > KELVIN) then + ! All liquid if temp above zero + frac_liq = 1.0 + else if (temp < KELVIN-40.0) then + ! All ice if temp is below -40C + frac_liq = 0.0 + else + ! linearly interpolate between T=0 and -40C + frac_liq = 1.0 - (KELVIN - temp) / 40.0 + end if + + end subroutine calc_liq_frac + + subroutine calc_reff(frac_liq, reff_rad) + ! the effective cloud radius is bounded between 10 and 20 microns + + real, intent(in) :: frac_liq + real, intent(out) :: reff_rad + + reff_rad = 10.0 * frac_liq + 20.0 * (1.0 - frac_liq) + ! units in microns this will be updated before passing into soc + + end subroutine calc_reff + + subroutine calc_rhcrit(p_full, p_surf, simple_rhcrit) + ! Get the RH needed as a threshold for the cloud fraction calc. + ! This is only requires for spookie_protocol=1 + real, intent(in) :: p_full, p_surf + real, intent(out) :: simple_rhcrit + + ! Calculate RHcrit as function of pressure + if (p_full > p_base) then + + simple_rhcrit = rhc_sfc - (rhc_sfc - rhc_base) * & + (p_surf - p_full) / (p_surf - p_base) + + else if (p_full > p_top) then + + simple_rhcrit = rhc_base - (rhc_base - rhc_top) * & + (p_base - p_full) / (p_base - p_top) + + else + simple_rhcrit = rhc_top + endif + + end subroutine calc_rhcrit + + subroutine calc_rh_min_max(p_full, p_surf, rh_min, rh_max) + + real, intent(in) :: p_full, p_surf + real, intent(out) :: rh_min, rh_max + + real :: layer + + ! calculate RH min and max as a function of pressure + + if (p_full > p_base) then + ! For the layer between the surface and cloud base (default is 700 hpa) + + layer = (p_surf - p_full) / (p_surf - p_base) + + ! correction step to update initial values + rh_min = rh_min_sfc - (rh_min_sfc - rh_min_base) * layer + rh_max = rh_max_sfc - (rh_max_sfc - rh_max_base) * layer + + else if ( p_full > p_top ) then + ! For the layer where the cloud is (base up to top) + + layer = (p_base - p_full) / (p_base - p_top) + rh_min = rh_min_base - ( rh_min_base - rh_min_top ) * layer + rh_max = rh_max_base - ( rh_max_base - rh_max_top ) * layer + + else + ! Above the cloud top above top + rh_min = rh_min_top + rh_max = rh_max_top + endif + + end subroutine calc_rh_min_max + + subroutine calc_cf(q_hum, qsat, cf, cca, rh, simple_rhcrit, rh_min, rh_max) + ! Calculate large scale (stratiform) cloud fraction + ! as a simple linear function of RH + + real, intent(in) :: q_hum, qsat + real, intent(in), optional :: simple_rhcrit, rh_min, rh_max + + real, intent(out) :: cf, rh, cca + + ! The environment RH + rh = q_hum / qsat + + if (spookie_protocol .eq. 1) then + cf = (rh - simple_rhcrit ) / (1.0 - simple_rhcrit) + else + cf = (rh - rh_min) / (rh_max - rh_min) + end if + + cf = MAX(0.0, MIN(1.0, cf)) + + ! include simple convective cloud fraction where present + ! This is currently not being used and array are zeros as + ! no convective cloud fraction is calculated + ! left in for future use + + !if (cca > 0.0) then + ! cf = MAX( cca_lower_limit, cf ) + !end if + + end subroutine calc_cf + + subroutine calc_mixing_ratio(p_full, cf, temp, qcl_rad) + + ! calculate cloud water content + + real , intent(in) :: p_full, cf, temp + real , intent(out) :: qcl_rad ! mixing ratio of cloud liquid + + real :: in_cloud_qcl + + IF (spookie_protocol .eq. 1) THEN + ! pressure dependent in_cloud_qcl + ! bounded between: + ! 1 g/kg at 1000hpa + ! 3e-4 g/kg at 200 hpa + in_cloud_qcl = 3.0e-4 + (1.0 - 3.0e-4) * (p_full - p_top) / 80000.0 + in_cloud_qcl = MAX ( 0.0, in_cloud_qcl) ! in g/kg + ELSE + ! temperatue dependent in_cloud_qcl + ! bounded between: + ! 3e-4 g/kg at 220 K + ! 0.2 g/kg at 280K + in_cloud_qcl = MIN(0.2, 0.2 * ( temp - 220. ) / ( 280. - 220. )) + in_cloud_qcl = MAX(3.0e-4, in_cloud_qcl) ! in g/kg + ENDIF + + qcl_rad = cf * in_cloud_qcl / 1000. ! convert to kg/kg + + end subroutine calc_mixing_ratio + + subroutine output_cloud_diags(cf, reff_rad, frac_liq, qcl_rad, rh_in_cf, & + simple_rhcrit, rh_min, Time) + + real, intent(in), dimension(:,:,:) :: cf, reff_rad, frac_liq, qcl_rad, & + rh_in_cf + real, intent(in), dimension(:,:,:), optional :: simple_rhcrit, rh_min + + type(time_type) , intent(in) :: Time + + logical :: used + + if ( id_cf > 0 ) then + used = send_data ( id_cf, cf, Time) + endif + + if ( id_reff_rad > 0 ) then + used = send_data ( id_reff_rad, reff_rad, Time) + endif + + if ( id_frac_liq > 0 ) then + used = send_data ( id_frac_liq, frac_liq, Time) + endif + + if ( id_qcl_rad > 0 ) then + used = send_data ( id_qcl_rad, qcl_rad, Time) + endif + + if ( id_rh_in_cf > 0 ) then + used = send_data ( id_rh_in_cf, rh_in_cf*100., Time) + endif + + if ( id_simple_rhcrit > 0 ) then + used = send_data ( id_simple_rhcrit, simple_rhcrit*100.0, Time) + endif + + if ( id_rh_min > 0 ) then + used = send_data ( id_rh_min, rh_min*100.0, Time) + endif + + end subroutine output_cloud_diags + +end module cloud_spookie_mod diff --git a/src/atmos_param/cloud_simple/large_scale_cloud.F90 b/src/atmos_param/cloud_simple/large_scale_cloud.F90 new file mode 100644 index 000000000..c26a78f4b --- /dev/null +++ b/src/atmos_param/cloud_simple/large_scale_cloud.F90 @@ -0,0 +1,334 @@ +module large_scale_cloud_mod + +#ifdef INTERNAL_FILE_NML + use mpp_mod, only: input_nml_file +#else + use fms_mod, only: open_namelist_file, close_file +#endif + + use fms_mod, only: stdlog, FATAL, WARNING, NOTE, error_mesg, & + uppercase, check_nml_error + use time_manager_mod, only: time_type + use sat_vapor_pres_mod, only: compute_qs, lookup_es + use diag_manager_mod, only: register_diag_field, send_data + use constants_mod, only: CP_AIR, GRAV, RDGAS, RVGAS, HLV, KAPPA, RADIUS, TFREEZE + + implicit none + + character(len=14), parameter :: mod_name = "ls_cloud" + + integer, parameter :: B_SPOOKIE=1, B_SUNDQVIST=2, B_LINEAR=3, & + B_SMITH=4, B_SLINGO=5, B_XR96=6 + integer, private :: cf_diag_formula = B_LINEAR + character(len=32) :: cf_diag_formula_name = 'linear' + + logical :: do_simple_rhcrit = .false. + logical :: do_fitted_rhcrit = .false. + logical :: do_adjust_cld_by_omega = .false. + logical :: do_poly_rhcrit = .false. + logical :: do_freezedry = .false. + + real :: rhcsfc = 0.95 + real :: rhc700 = 0.7 + real :: rhc200 = 0.3 + + ! Parameters to control the fitted critical RH profile + real :: rhc_surf = 0.8 + real :: rhc_top = 0.4 + real :: n_rhc = 3.1 + + ! Parameters for the freeze-dry problem in polar region + real :: qv_polar_val = 0.003 ! kg/kg + real :: freezedry_power = 2.5 + + ! Parameters to control linear coefficient profile + real :: linear_a_surf = 42 + real :: linear_a_top = 12 + real :: linear_power = 8.5 + + ! For slingo80 scheme + real :: slingo_rhc_low = 0.8 + real :: slingo_rhc_mid = 0.65 + real :: slingo_rhc_high = 0.8 + + ! For cloud adjustment by omega + real :: omega_adj_threshold = 0.1 !Pa/s + real :: adj_pres_threshold = 7.0e4 !Pa + + integer :: id_rhcrit + + namelist /large_scale_cloud_nml/ & + rhcsfc, rhc700, rhc200, & + do_fitted_rhcrit, do_poly_rhcrit, & + rhc_surf, rhc_top, n_rhc, & + cf_diag_formula_name, & + linear_a_surf, linear_a_top, linear_power, & + slingo_rhc_low, slingo_rhc_mid, slingo_rhc_high, & + do_adjust_cld_by_omega, omega_adj_threshold, adj_pres_threshold, & + do_freezedry, qv_polar_val, freezedry_power + + contains + + subroutine large_scale_cloud_init(axes, Time) + type(time_type), intent(in) :: Time + integer, intent(in), dimension(4) :: axes + integer :: io, ierr, nml_unit, stdlog_unit + character(len=32) :: method_str = '' + +#ifdef INTERNAL_FILE_NML + read(input_nml_file, nml=large_scale_cloud_nml, iostat=io) + ierr = check_nml_error(io, 'large_scale_cloud_nml') +#else + if (file_exist('input.nml')) then + nml_unit = open_namelist_file() + ierr = 1 + do while (ierr /= 0) + read(nml_unit, nml=large_scale_cloud_nml, iostat=io, end=10) + ierr = check_nml_error(io, 'large_scale_cloud_nml') + enddo +10 call close_file(nml_unit) + endif +#endif + stdlog_unit = stdlog() + write(stdlog_unit, large_scale_cloud_nml) + + ! Select cloud fraction diag formula + method_str = uppercase(trim(cf_diag_formula_name)) + + if(method_str == 'SPOOKIE') then + cf_diag_formula = B_SPOOKIE + call error_mesg(mod_name, 'Using default SPOOKIE cloud fraction diagnostic formula.', NOTE) + + else if(method_str == 'SUNDQVIST') then + cf_diag_formula = B_SUNDQVIST + call error_mesg(mod_name, 'Using Sundqvist (1989) cloud fraction diagnostic formula.', NOTE) + + else if(method_str == 'LINEAR') then + cf_diag_formula = B_LINEAR + call error_mesg(mod_name, 'Using linear cloud fraction diagnostic formula.', NOTE) + + else if(method_str == 'SMITH') then + cf_diag_formula = B_SMITH + call error_mesg(mod_name, 'Using Smith (1990) cloud fraction diagnostic formula.', NOTE) + + else if(method_str == 'SLINGO') then + cf_diag_formula = B_SLINGO + call error_mesg(mod_name, 'Using Slingo (1980) cloud fraction diagnostic formula.', NOTE) + + else if(method_str == 'XR96') then + cf_diag_formula = B_XR96 + call error_mesg(mod_name, 'Using Xu and Krueger (1996) cloud fraction diagnostic formula.', NOTE) + + else + call error_mesg(mod_name, '"'//trim(cf_diag_formula_name)//'"'// & + ' is not a valid cloud fraction diagnostic formula.', FATAL) + endif + + if (cf_diag_formula .eq. B_SPOOKIE .or. cf_diag_formula .eq. B_SUNDQVIST & + .or. cf_diag_formula .eq. B_SMITH) then + do_simple_rhcrit = .true. + else + do_simple_rhcrit = .false. + end if + + if(do_simple_rhcrit) then + id_rhcrit = register_diag_field (mod_name, 'rhcrit', axes(1:3), Time, & + 'critical relative humidity', '%') + end if + + end subroutine large_scale_cloud_init + + subroutine large_scale_cloud_diag(pfull, ps, rh, q_hum, qsat, qcl_rad, wg_full, cf, Time) + real, intent(in), dimension(:,:,:) :: pfull, rh, q_hum, qsat, qcl_rad, wg_full + real, intent(in), dimension(:,:) :: ps + real, intent(out), dimension(:,:,:) :: cf + type(time_type), intent(in) :: Time + real, dimension(size(rh,1), size(rh,2), size(rh,3)) :: rhcrit + logical :: used + + if (do_simple_rhcrit) then + call calc_rhcrit(pfull, rhcrit) + end if + + call calc_large_scale_cld_frac(pfull, ps, rh, q_hum, qsat, rhcrit, qcl_rad, cf) + + if(do_adjust_cld_by_omega) then + call adjust_cld_by_omega(pfull, wg_full, cf) + end if + + if(do_freezedry) then + call freezedry_adjustment(pfull, ps, cf, q_hum) + end if + + if (id_rhcrit > 0) then + used = send_data (id_rhcrit, rhcrit*100.0, Time) + endif + + end subroutine large_scale_cloud_diag + + subroutine calc_rhcrit(p_full, rhcrit) + !get the RH needed as a threshold for the cloud fraction calc. + real, intent(in), dimension(:,:,:) :: p_full + real, intent(out), dimension(:,:,:) :: rhcrit + real :: p_surf + real, dimension(size(p_full,1), size(p_full,2), size(p_full,3)) :: sigma + + real :: rhc1, rhc2, zrhc + p_surf = 1e5 + + if (do_fitted_rhcrit) then + rhcrit = rhc_top + (rhc_surf - rhc_top) * EXP(1.0 - (p_surf/p_full)**n_rhc) + else if (do_poly_rhcrit) then + rhc1 = 0.8 + rhc2 = 1.73 + zrhc = 0.95 !0.85 + sigma = p_full / p_surf + rhcrit = zrhc - rhc1*sigma * (1.0-sigma) * (1.0 + rhc2*(sigma-0.5)) + !rhcrit = poly_rhc_surf*sigma + poly_rhc_top*(1.0-sigma) + sigma * (1.0-sigma) * (1.0 + rhc2*(sigma-0.5)) + else + where (p_full > 7.0e4) + rhcrit = rhcsfc - (rhcsfc - rhc700) * (p_surf - p_full) / (p_surf - 7.0e4) + elsewhere (p_full > 2.0e4) + rhcrit = rhc700 - (rhc700 - rhc200) * (7.0e4 - p_full) / 5.0e4 + elsewhere + rhcrit = rhc200 + endwhere + end if + + end subroutine calc_rhcrit + + subroutine adjust_cld_by_omega(p_full, wg_full, cf) + real, intent(in), dimension(:,:,:) :: p_full, wg_full + real, intent(inout), dimension(:,:,:) :: cf + + where (p_full>adj_pres_threshold .and. omega_adj_threshold>wg_full .and. wg_full>0.0) + cf = min(1.0, (omega_adj_threshold-wg_full)/omega_adj_threshold) * cf + end where + + where (p_full>adj_pres_threshold .and. wg_full>=omega_adj_threshold) + cf = 0.0 + end where + + end subroutine adjust_cld_by_omega + + subroutine freezedry_adjustment(p_full, psg, cf, q_hum) + real, intent(in), dimension(:,:,:) :: p_full, q_hum + real, intent(in), dimension(:,:) :: psg + real, intent(inout), dimension(:,:,:) :: cf + integer :: k + real, dimension(size(cf,1), size(cf,2)) :: qv_k + + ! VAVRUS and WALISER, 2008, An Improved Parameterization for Simulating + ! Arctic Cloud Amount in the CCSM3 Climate Model, + ! Journal of Climate, DOI: 10.1175/2008JCLI2299.1 + ! + ! A difference from the paper is we adjust the clouds not only near the + ! surface but through all the levels of atmosphere + + do k=1,size(p_full,3) + qv_k = (p_full(:,:,k) / psg)**freezedry_power * qv_polar_val + cf(:,:,k) = cf(:,:,k) * MAX(0.15, MIN(1.0, q_hum(:,:,k) / qv_k)) + end do + + end subroutine freezedry_adjustment + + subroutine calc_large_scale_cld_frac(pfull, ps, rh, q_hum, qsat, rhcrit, qcl_rad, cf) + ! Calculate large scale (stratiform) cloud fraction + real, intent(in), dimension(:,:,:) :: pfull, rh, q_hum, qsat, rhcrit, qcl_rad + real, intent(in), dimension(:,:) :: ps + real, intent(out), dimension(:,:,:) :: cf + real, dimension(size(pfull,1), size(pfull,2), size(pfull,3)) :: rhc + real :: mid_top, mid_base, p_para, alpha_0, gamma ! For Xu and Krueger (1996) + integer :: i, j, k + + select case(cf_diag_formula) + case(B_SPOOKIE) + cf = (rh - rhcrit) / (1.0 - rhcrit) + + case(B_SUNDQVIST) + ! Refer to: Sundqvist et al., 1989, MWR, Condensation and Cloud Parameterization + ! Studies with a Mesoscale Numerical Weather Prediction Model + ! https://journals.ametsoc.org/doi/10.1175/1520-0493(1989)117%3C1641:CACPSW%3E2.0.CO%3B2 + ! + ! Eq (3.13) in the paper above + + cf = 1.0 - ((1.0 - MIN(rh,1.0)) / (1.0 - rhcrit))**0.5 + + case(B_SMITH) + ! Refer to: Smith (1990). A scheme for predicting layer clouds and their water + ! in a general circulation model. QJRMS, 116(492), 435-460. + + cf = 1.0 - (3.0 / sqrt(2.0) * (1.0 - MIN(rh,1.0))/(1.0 - rhcrit))**(2.0/3.0) + + case(B_SLINGO) + ! Refer to: Slingo (1980). A cloud parametrization scheme derived from + ! GATE data for use with a numerical model. QJRMS, 106(450), 747-770. + + mid_base = 8.0e4 + mid_top = 4.0e4 + rhc = 0.0 + + where (pfull > mid_base) + rhc = slingo_rhc_low + elsewhere (pfull < mid_top) + rhc = slingo_rhc_high + elsewhere + rhc = slingo_rhc_mid + end where + + where (rh= 273.15 K and with respect to ice if +! T < 273.15 K. +! * The value of rhl is interpreted to be the relative humidity with +! respect to liquid water +! * The value of rhs is interpreted to be the relative humidity with +! respect to ice +! - ldl is an optional logical flag. If true, the lifting deposition +! level (LDL) is returned instead of the LCL. +! - min_lcl_ldl is an optional logical flag. If true, the minimum of the +! LCL and LDL is returned. + +module lcl_mod + +contains + +function lcl(p,T,rh,rhl,rhs,return_ldl,return_min_lcl_ldl) + + implicit none + + ! In + double precision, intent(in) :: p, T + double precision, intent(in), optional :: rh, rhl, rhs + logical, intent(in), optional :: return_ldl, return_min_lcl_ldl + + ! Out + double precision :: lcl + + logical :: liquid2, solid2 + integer(kind=4) :: error + + double precision :: Ttrip, ptrip, E0v, E0s, ggr, rgasa, & + rgasv, cva, cvv, cvl, cvs, cpa, cpv, al, bl, cl, as, bs, cs, & + pv, qv, cpm, rgasm, rh2, rhl2, rhs2, ldl + integer :: rh_counter + logical :: return_ldl2, return_min_lcl_ldl2 + + ! Set defaults for options + return_ldl2 = .false. + return_min_lcl_ldl2 = .false. + + if (present(return_ldl)) return_ldl2 = return_ldl + if (present(return_min_lcl_ldl)) return_min_lcl_ldl2 = return_min_lcl_ldl + + ! Parameters + Ttrip = 273.16 ! K + ptrip = 611.65 ! Pa + E0v = 2.3740e6 ! J/kg + E0s = 0.3337e6 ! J/kg + ggr = 9.81 ! m/s^2 + rgasa = 287.04 ! J/kg/K + rgasv = 461. ! J/kg/K + cva = 719. ! J/kg/K + cvv = 1418. ! J/kg/K + cvl = 4119. ! J/kg/K + cvs = 1861. ! J/kg/K + cpa = cva + rgasa + cpv = cvv + rgasv + + ! Ensure that, at most, one of liquid and solid are true + if (return_ldl2 .and. return_min_lcl_ldl2) then + print *,'Error in lcl: At most, only one of return_ldl and return_min_lcl_ldl can be true' + stop + end if + + ! Calculate pv from rh, rhl, or rhs + rh_counter = 0 + if (present(rh )) rh_counter = rh_counter + 1 + if (present(rhl)) rh_counter = rh_counter + 1 + if (present(rhs)) rh_counter = rh_counter + 1 + if (rh_counter .ne. 1) then + print *,'Error in lcl: Exactly one of rh, rhl, and rhs must be specified' + stop + end if + + if (present(rh)) then + rh2 = rh + ! The variable rh is assumed to be + ! with respect to liquid if T > Ttrip and + ! with respect to solid if T < Ttrip + if (T > Ttrip) then + pv = rh2 * pvstarl(T) + else + pv = rh2 * pvstars(T) + end if + rhl2 = pv / pvstarl(T) + rhs2 = pv / pvstars(T) + else if (present(rhl)) then + rhl2 = rhl + pv = rhl2 * pvstarl(T) + rhs2 = pv / pvstars(T) + if (T > Ttrip) then + rh2 = rhl2 + else + rh2 = rhs2 + end if + else if (present(rhs)) then + rhs2 = rhs + pv = rhs2 * pvstars(T) + rhl2 = pv / pvstarl(T) + if (T > Ttrip) then + rh2 = rhl2 + else + rh2 = rhs2 + end if + end if + if (pv > p) then + print *,'Warning in lcl: pv exceeds p, returning -9999' + lcl = -9999. + return + end if + + ! Calculate lcl_liquid and lcl_solid + qv = rgasa*pv / (rgasv*p + (rgasa-rgasv)*pv) + rgasm = (1.-qv)*rgasa + qv*rgasv + cpm = (1.-qv)*cpa + qv*cpv + if (rh2==0) then + lcl = cpm*T/ggr + return + end if + al = -(cpv-cvl)/rgasv + cpm/rgasm + bl = -(E0v-(cvv-cvl)*Ttrip)/(rgasv*T) + !cl = pv/pvstarl(T)*exp(-(E0v-(cvv-cvl)*Ttrip)/(rgasv*T)) + cl = pv/pvstarl(T)*exp(bl) + + as = -(cpv-cvs)/rgasv + cpm/rgasm + bs = -(E0v+E0s-(cvv-cvs)*Ttrip)/(rgasv*T) + !cs = pv/pvstars(T)*exp(-(E0v+E0s-(cvv-cvs)*Ttrip)/(rgasv*T)) + cs = pv/pvstars(T)*exp(bs) + + lcl = cpm*T/ggr*( 1. - bl/(al*wapr(bl/al*cl**(1./al),-1,error,0)) ) + ldl = cpm*T/ggr*( 1. - bs/(as*wapr(bs/as*cs**(1./as),-1,error,0)) ) + + ! Return either the LCL or the LDL + if (return_ldl2 .and. return_min_lcl_ldl2) then + print *,'Error in lcl: return_ldl and return_min_lcl_ldl cannot both be true' + stop + else if (return_ldl2) then + lcl = ldl + else if (return_min_lcl_ldl2) then + lcl = min(lcl,ldl) + end if + + contains + + ! The saturation vapor pressure over liquid water + function pvstarl(T) + + implicit none + + double precision :: T ! In + double precision :: pvstarl ! Out + + pvstarl = ptrip * (T/Ttrip)**((cpv-cvl)/rgasv) * & + exp( (E0v - (cvv-cvl)*Ttrip) / rgasv * (1./Ttrip - 1./T) ) + + end function pvstarl + + ! The saturation vapor pressure over solid ice + function pvstars(T) + + implicit none + + double precision :: T ! In + double precision :: pvstars ! Out + + pvstars = ptrip * (T/Ttrip)**((cpv-cvs)/rgasv) * & + exp( (E0v + E0s - (cvv-cvs)*Ttrip) / rgasv * (1./Ttrip - 1./T) ) + + end function pvstars + +end function lcl + +function bisect ( xx, nb, ner, l ) + +!*****************************************************************************80 +! +!! BISECT approximates the W function using bisection. +! +! Discussion: +! +! The parameter TOL, which determines the accuracy of the bisection +! method, is calculated using NBITS (assuming the final bit is lost +! due to rounding error). +! +! N0 is the maximum number of iterations used in the bisection +! method. +! +! For XX close to 0 for Wp, the exponential approximation is used. +! The approximation is exact to O(XX^8) so, depending on the value +! of NBITS, the range of application of this formula varies. Outside +! this range, the usual bisection method is used. +! +! Licensing: +! +! This code is distributed under the GNU LGPL license. +! +! Modified: +! +! 15 June 2014 +! +! Author: +! +! Original FORTRAN77 version by Andrew Barry, S. J. Barry, +! Patricia Culligan-Hensley. +! This FORTRAN90 version by John Burkardt. +! +! Reference: +! +! Andrew Barry, S. J. Barry, Patricia Culligan-Hensley, +! Algorithm 743: WAPR - A Fortran routine for calculating real +! values of the W-function, +! ACM Transactions on Mathematical Software, +! Volume 21, Number 2, June 1995, pages 172-181. +! +! Parameters: +! +! Input, double precision :: XX, the argument. +! +! Input, integer ( kind = 4 ) NB, indicates the branch of the W function. +! 0, the upper branch; +! nonzero, the lower branch. +! +! Output, integer ( kind = 4 ) NER, the error flag. +! 0, success; +! 1, the routine did not converge. Perhaps reduce NBITS and try again. +! +! Input, integer ( kind = 4 ) L, the offset indicator. +! 1, XX represents the offset of the argument from -exp(-1). +! not 1, XX is the actual argument. +! +! Output, double precision :: BISECT, the value of W(X), as determined +! + implicit none + + double precision :: bisect +! double precision :: crude + double precision :: d + double precision :: f + double precision :: fd + integer ( kind = 4 ) i + integer ( kind = 4 ) l + integer ( kind = 4 ) n0 + parameter ( n0 = 500 ) + integer ( kind = 4 ) nb + integer ( kind = 4 ), save :: nbits = 0 + integer ( kind = 4 ) ner + double precision :: r + double precision :: test + double precision :: tol + double precision :: u + double precision :: x + double precision :: xx + + bisect = 0.0D+00 + ner = 0 + + if ( nbits == 0 ) then + call nbits_compute ( nbits ) + end if + + if ( l == 1 ) then + x = xx - exp ( -1.0D+00 ) + else + x = xx + end if + + if ( nb == 0 ) then + + test = 1.0D+00 / ( 2.0D+00 ** nbits ) ** ( 1.0D+00 / 7.0D+00 ) + + if ( abs ( x ) < test ) then + + bisect = x & + * exp ( - x & + * exp ( - x & + * exp ( - x & + * exp ( - x & + * exp ( - x & + * exp ( - x )))))) + + return + + else + + u = crude ( x, nb ) + 1.0D-03 + tol = abs ( u ) / 2.0D+00 ** nbits + d = max ( u - 2.0D-03, -1.0D+00 ) + + do i = 1, n0 + + r = 0.5D+00 * ( u - d ) + bisect = d + r +! +! Find root using w*exp(w)-x to avoid ln(0) error. +! + if ( x < exp ( 1.0D+00 ) ) then + + f = bisect * exp ( bisect ) - x + fd = d * exp ( d ) - x +! +! Find root using ln(w/x)+w to avoid overflow error. +! + else + + f = log ( bisect / x ) + bisect + fd = log ( d / x ) + d + + end if + + if ( f == 0.0D+00 ) then + return + end if + + if ( abs ( r ) <= tol ) then + return + end if + + if ( 0.0D+00 < fd * f ) then + d = bisect + else + u = bisect + end if + + end do + + end if + + else + + d = crude ( x, nb ) - 1.0D-03 + u = min ( d + 2.0D-03, -1.0D+00 ) + tol = abs ( u ) / 2.0D+00 ** nbits + + do i = 1, n0 + + r = 0.5D+00 * ( u - d ) + bisect = d + r + f = bisect * exp ( bisect ) - x + + if ( f == 0.0D+00 ) then + return + end if + + if ( abs ( r ) <= tol ) then + return + end if + + fd = d * exp ( d ) - x + + if ( 0.0D+00 < fd * f ) then + d = bisect + else + u = bisect + end if + + end do + + end if +! +! The iteration did not converge. +! + ner = 1 + + return +end function bisect + +function crude ( xx, nb ) + +!*****************************************************************************80 +! +!! CRUDE returns a crude approximation for the W function. +! +! Licensing: +! +! This code is distributed under the GNU LGPL license. +! +! Modified: +! +! 15 June 2014 +! +! Author: +! +! Original FORTRAN77 version by Andrew Barry, S. J. Barry, +! Patricia Culligan-Hensley. +! This FORTRAN90 version by John Burkardt. +! +! Reference: +! +! Andrew Barry, S. J. Barry, Patricia Culligan-Hensley, +! Algorithm 743: WAPR - A Fortran routine for calculating real +! values of the W-function, +! ACM Transactions on Mathematical Software, +! Volume 21, Number 2, June 1995, pages 172-181. +! +! Parameters: +! +! Input, double precision :: XX, the argument. +! +! Input, integer ( kind = 4 ) NB, indicates the desired branch. +! * 0, the upper branch; +! * nonzero, the lower branch. +! +! Output, double precision :: CRUDE, the crude approximation to W at XX. +! + implicit none + + double precision :: an2 + double precision :: c13 + double precision :: crude + double precision :: em + double precision :: em2 + double precision :: em9 + double precision :: eta + integer ( kind = 4 ) init + integer ( kind = 4 ) nb + double precision :: reta + double precision :: s2 + double precision :: s21 + double precision :: s22 + double precision :: s23 + double precision :: t + double precision :: ts + double precision :: xx + double precision :: zl + + save c13 + save em + save em2 + save em9 + save init + save s2 + save s21 + save s22 + save s23 + + data init / 0 / + + crude = 0.0D+00 +! +! Various mathematical constants. +! + if ( init == 0 ) then + init = 1 + em = - exp ( -1.0D+00 ) + em9 = - exp ( -9.0D+00 ) + c13 = 1.0D+00 / 3.0D+00 + em2 = 2.0D+00 / em + s2 = sqrt ( 2.0D+00 ) + s21 = 2.0D+00 * s2 - 3.0D+00 + s22 = 4.0D+00 - 3.0D+00 * s2 + s23 = s2 - 2.0D+00 + end if +! +! Crude Wp. +! + if ( nb == 0 ) then + + if ( xx <= 20.0D+00 ) then + reta = s2 * sqrt ( 1.0D+00 - xx / em ) + an2 = 4.612634277343749D+00 * sqrt ( sqrt ( reta + & + 1.09556884765625D+00 ) ) + crude = reta / ( 1.0D+00 + reta / ( 3.0D+00 & + + ( s21 * an2 + s22 ) * reta / ( s23 * ( an2 + reta )))) - 1.0D+00 + else + zl = log ( xx ) + crude = log ( xx / log ( xx & + / zl ** exp ( -1.124491989777808D+00 / & + ( 0.4225028202459761D+00 + zl )))) + end if + + else +! +! Crude Wm. +! + if ( xx <= em9 ) then + zl = log ( -xx ) + t = -1.0D+00 - zl + ts = sqrt ( t ) + crude = zl - ( 2.0D+00 * ts ) / ( s2 + ( c13 - t & + / ( 2.7D+02 + ts * 127.0471381349219D+00 ) ) * ts ) + else + zl = log ( -xx ) + eta = 2.0D+00 - em2 * xx + crude = log ( xx / log ( - xx / ( ( 1.0D+00 & + - 0.5043921323068457D+00 * ( zl + 1.0D+00 ) ) & + * ( sqrt ( eta ) + eta / 3.0D+00 ) + 1.0D+00 ) ) ) + end if + + end if + + return +end function crude + +subroutine nbits_compute ( nbits ) + +!*****************************************************************************80 +! +!! NBITS_COMPUTE computes the mantissa length minus one. +! +! Discussion: +! +! NBITS is the number of bits (less 1) in the mantissa of the +! floating point number number representation of your machine. +! It is used to determine the level of accuracy to which the W +! function should be calculated. +! +! Most machines use a 24-bit matissa for single precision and +! 53-56 bits for double precision ::. The IEEE standard is 53 +! bits. The Fujitsu VP2200 uses 56 bits. Long word length +! machines vary, e.g., the Cray X/MP has a 48-bit mantissa for +! single precision. +! +! Licensing: +! +! This code is distributed under the GNU LGPL license. +! +! Modified: +! +! 15 June 2014 +! +! Author: +! +! Original FORTRAN77 version by Andrew Barry, S. J. Barry, +! Patricia Culligan-Hensley. +! This FORTRAN90 version by John Burkardt. +! +! Reference: +! +! Andrew Barry, S. J. Barry, Patricia Culligan-Hensley, +! Algorithm 743: WAPR - A Fortran routine for calculating real +! values of the W-function, +! ACM Transactions on Mathematical Software, +! Volume 21, Number 2, June 1995, pages 172-181. +! +! Parameters: +! +! Output, integer ( kind = 4 ) NBITS, the mantissa length, in bits, +! minus one. +! + implicit none + + double precision :: b + integer ( kind = 4 ) i + integer ( kind = 4 ) nbits + double precision :: v + + nbits = 0 + + b = 1.0D+00 + + do + + b = b / 2.0D+00 + v = b + 1.0D+00 + + if ( v == 1.0D+00 ) then + return + end if + + nbits = nbits + 1 + + end do + + return +end subroutine nbits_compute + +subroutine timestamp ( ) + +!*****************************************************************************80 +! +!! TIMESTAMP prints the current YMDHMS date as a time stamp. +! +! Example: +! +! 31 May 2001 9:45:54.872 AM +! +! Licensing: +! +! This code is distributed under the GNU LGPL license. +! +! Modified: +! +! 18 May 2013 +! +! Author: +! +! John Burkardt +! +! Parameters: +! +! None +! + implicit none + + character ( len = 8 ) ampm + integer ( kind = 4 ) d + integer ( kind = 4 ) h + integer ( kind = 4 ) m + integer ( kind = 4 ) mm + character ( len = 9 ), parameter, dimension(12) :: month = (/ & + 'January ', 'February ', 'March ', 'April ', & + 'May ', 'June ', 'July ', 'August ', & + 'September', 'October ', 'November ', 'December ' /) + integer ( kind = 4 ) n + integer ( kind = 4 ) s + integer ( kind = 4 ) values(8) + integer ( kind = 4 ) y + + call date_and_time ( values = values ) + + y = values(1) + m = values(2) + d = values(3) + h = values(5) + n = values(6) + s = values(7) + mm = values(8) + + if ( h < 12 ) then + ampm = 'AM' + else if ( h == 12 ) then + if ( n == 0 .and. s == 0 ) then + ampm = 'Noon' + else + ampm = 'PM' + end if + else + h = h - 12 + if ( h < 12 ) then + ampm = 'PM' + else if ( h == 12 ) then + if ( n == 0 .and. s == 0 ) then + ampm = 'Midnight' + else + ampm = 'AM' + end if + end if + end if + + write ( *, '(i2.2,1x,a,1x,i4,2x,i2,a1,i2.2,a1,i2.2,a1,i3.3,1x,a)' ) & + d, trim ( month(m) ), y, h, ':', n, ':', s, '.', mm, trim ( ampm ) + + return +end subroutine timestamp + +function wapr ( x, nb, nerror, l ) + +!*****************************************************************************80 +! +!! WAPR approximates the W function. +! +! Discussion: +! +! The call will fail if the input value X is out of range. +! The range requirement for the upper branch is: +! -exp(-1) <= X. +! The range requirement for the lower branch is: +! -exp(-1) < X < 0. +! +! Licensing: +! +! This code is distributed under the GNU LGPL license. +! +! Modified: +! +! 15 June 2014 +! +! Author: +! +! Original FORTRAN77 version by Andrew Barry, S. J. Barry, +! Patricia Culligan-Hensley. +! This FORTRAN90 version by John Burkardt. +! +! Reference: +! +! Andrew Barry, S. J. Barry, Patricia Culligan-Hensley, +! Algorithm 743: WAPR - A Fortran routine for calculating real +! values of the W-function, +! ACM Transactions on Mathematical Software, +! Volume 21, Number 2, June 1995, pages 172-181. +! +! Parameters: +! +! Input, double precision :: X, the argument. +! +! Input, integer ( kind = 4 ) NB, indicates the desired branch. +! * 0, the upper branch; +! * nonzero, the lower branch. +! +! Output, integer ( kind = 4 ) NERROR, the error flag. +! * 0, successful call. +! * 1, failure, the input X is out of range. +! +! Input, integer ( kind = 4 ) L, indicates the interpretation of X. +! * 1, X is actually the offset from -(exp-1), so compute W(X-exp(-1)). +! * not 1, X is the argument; compute W(X); +! +! Output, double precision :: WAPR, the approximate value of W(X). +! + implicit none + + double precision :: an2 + double precision :: an3 + double precision :: an4 + double precision :: an5 + double precision :: an6 + double precision :: c13 + double precision :: c23 + double precision :: d12 + double precision :: delx + double precision :: em + double precision :: em2 + double precision :: em9 + double precision :: eta + integer ( kind = 4 ) i + integer ( kind = 4 ) init + integer ( kind = 4 ) l + integer ( kind = 4 ) m + integer ( kind = 4 ) nb + integer ( kind = 4 ) nbits + integer ( kind = 4 ) nerror + integer ( kind = 4 ) niter + double precision :: reta + double precision :: s2 + double precision :: s21 + double precision :: s22 + double precision :: s23 + double precision :: t + double precision :: tb + double precision :: tb2 + double precision :: temp + double precision :: temp2 + double precision :: ts + double precision :: wapr + double precision :: x + double precision :: x0 + double precision :: x1 + double precision :: xx + double precision :: zl + double precision :: zn + + save an3 + save an4 + save an5 + save an6 + save c13 + save c23 + save d12 + save em + save em2 + save em9 + save init + save nbits + save niter + save s2 + save s21 + save s22 + save s23 + save tb + save tb2 + save x0 + save x1 + + data init / 0 / + data niter / 1 / + + wapr = 0.0D+00 + nerror = 0 + + if ( init == 0 ) then + + init = 1 + + call nbits_compute ( nbits ) + + if ( 56 <= nbits ) then + niter = 2 + end if +! +! Various mathematical constants. +! + em = -exp ( -1.0D+00 ) + em9 = -exp ( -9.0D+00 ) + c13 = 1.0D+00 / 3.0D+00 + c23 = 2.0D+00 * c13 + em2 = 2.0D+00 / em + d12 = -em2 + tb = 0.5D+00 ** nbits + tb2 = sqrt ( tb ) + x0 = tb ** ( 1.0D+00 / 6.0D+00 ) * 0.5D+00 + x1 = ( 1.0D+00 - 17.0D+00 * tb ** ( 2.0D+00 / 7.0D+00 ) ) * em + an3 = 8.0D+00 / 3.0D+00 + an4 = 135.0D+00 / 83.0D+00 + an5 = 166.0D+00 / 39.0D+00 + an6 = 3167.0D+00 / 3549.0D+00 + s2 = sqrt ( 2.0D+00 ) + s21 = 2.0D+00 * s2 - 3.0D+00 + s22 = 4.0D+00 - 3.0D+00 * s2 + s23 = s2 - 2.0D+00 + + end if + + if ( l == 1 ) then + + delx = x + + if ( delx < 0.0D+00 ) then + nerror = 1 + write ( *, '(a)' ) '' + write ( *, '(a)' ) 'WAPR - Fatal error!' + write ( *, '(a)' ) ' The offset X is negative.' + write ( *, '(a)' ) ' It must be nonnegative.' + stop 1 + end if + + xx = x + em + + else + + if ( x < em ) then + nerror = 1 + return + else if ( x == em ) then + wapr = -1.0D+00 + return + end if + + xx = x + delx = xx - em + + end if + + if ( nb == 0 ) then +! +! Calculations for Wp. +! + if ( abs ( xx ) <= x0 ) then + wapr = xx / ( 1.0D+00 + xx / ( 1.0D+00 + xx & + / ( 2.0D+00 + xx / ( 0.6D+00 + 0.34D+00 * xx )))) + return + else if ( xx <= x1 ) then + reta = sqrt ( d12 * delx ) + wapr = reta / ( 1.0D+00 + reta / ( 3.0D+00 + reta / ( reta & + / ( an4 + reta / ( reta * an6 + an5 ) ) + an3 ) ) ) & + - 1.0D+00 + return + else if ( xx <= 20.0D+00 ) then + reta = s2 * sqrt ( 1.0D+00 - xx / em ) + an2 = 4.612634277343749D+00 * sqrt ( sqrt ( reta + & + 1.09556884765625D+00 )) + wapr = reta / ( 1.0D+00 + reta / ( 3.0D+00 + ( s21 * an2 & + + s22 ) * reta / ( s23 * ( an2 + reta )))) - 1.0D+00 + else + zl = log ( xx ) + wapr = log ( xx / log ( xx & + / zl ** exp ( -1.124491989777808D+00 / & + ( 0.4225028202459761D+00 + zl )))) + end if +! +! Calculations for Wm. +! + else + + if ( 0.0D+00 <= xx ) then + nerror = 1 + return + else if ( xx <= x1 ) then + reta = sqrt ( d12 * delx ) + wapr = reta / ( reta / ( 3.0D+00 + reta / ( reta / ( an4 & + + reta / ( reta * an6 - an5 ) ) - an3 ) ) - 1.0D+00 ) - 1.0D+00 + return + else if ( xx <= em9 ) then + zl = log ( -xx ) + t = -1.0D+00 - zl + ts = sqrt ( t ) + wapr = zl - ( 2.0D+00 * ts ) / ( s2 + ( c13 - t & + / ( 270.0D+00 + ts * 127.0471381349219D+00 )) * ts ) + else + zl = log ( -xx ) + eta = 2.0D+00 - em2 * xx + wapr = log ( xx / log ( -xx / ( ( 1.0D+00 & + - 0.5043921323068457D+00 * ( zl + 1.0D+00 ) ) & + * ( sqrt ( eta ) + eta / 3.0D+00 ) + 1.0D+00 ))) + end if + + end if + + do i = 1, niter + zn = log ( xx / wapr ) - wapr + temp = 1.0D+00 + wapr + temp2 = temp + c23 * zn + temp2 = 2.0D+00 * temp * temp2 + wapr = wapr * ( 1.0D+00 + ( zn / temp ) * ( temp2 - zn ) & + / ( temp2 - 2.0D+00 * zn ) ) + end do + + return +end function wapr + +end module lcl_mod diff --git a/src/atmos_param/cloud_simple/marine_strat_cloud.F90 b/src/atmos_param/cloud_simple/marine_strat_cloud.F90 new file mode 100644 index 000000000..007af2e5f --- /dev/null +++ b/src/atmos_param/cloud_simple/marine_strat_cloud.F90 @@ -0,0 +1,593 @@ +module marine_strat_cloud_mod + +#ifdef INTERNAL_FILE_NML + use mpp_mod, only: input_nml_file +#else + use fms_mod, only: open_namelist_file, close_file +#endif + + use fms_mod, only: stdlog, FATAL, WARNING, NOTE, error_mesg, & + uppercase, check_nml_error + use time_manager_mod, only: time_type + use sat_vapor_pres_mod, only: compute_qs, lookup_es + use diag_manager_mod, only: register_diag_field, send_data + use constants_mod, only: CP_AIR, GRAV, RDGAS, RVGAS, HLV, KAPPA, RADIUS, TFREEZE + use lcl_mod, only: lcl + + implicit none + + character(len=14), parameter :: mod_name = "strat_cloud" + + character(len=32) :: sc_diag_method = 'Park_ELF' + logical :: intermediate_outputs_diags = .false. + real :: dthdp_min_threshold = -0.05 ! K/hPa, which is -0.125 in CESM1.2.1 + + ! ----- outputs for EIS, ECTEI and ELF diagnostics ----- ! + integer :: id_theta, id_dthdp, id_lts, id_eis, id_ectei, id_zlcl, & + id_gamma_850, id_gamma_DL, id_gamma_700, id_z700, & + id_zinv, id_ELF, id_beta1, id_beta2, id_IS, id_DS, id_alpha, & + id_low_cld_amt_park, id_marine_strat + + ! Define constants for Earth mass and Newtonian gravational constant + ! Refer to: https://github.com/Unidata/MetPy/ --> src/metpy/constants.py + real :: EARTH_MASS = 5.9722e24 ! kg + ! Refer to: https://physics.nist.gov/cgi-bin/cuu/Value?bg + real :: GRAV_CONST = 6.674e-11 ! m^3 / kg / s^2 + + ! Linear coefficient for Park_ELF scheme + real :: park_a = 1.272 + real :: park_b = -0.366 + + namelist /marine_strat_cloud_nml/ & + sc_diag_method, intermediate_outputs_diags, dthdp_min_threshold, & + park_a, park_b + + contains + + subroutine marine_strat_cloud_init(axes, Time) + type(time_type), intent(in) :: Time + integer, intent(in), dimension(4) :: axes + integer :: io, ierr, nml_unit, stdlog_unit + character(len=32) :: method_str = '' + +#ifdef INTERNAL_FILE_NML + read(input_nml_file, nml=marine_strat_cloud_nml, iostat=io) + ierr = check_nml_error(io, 'marine_strat_cloud_nml') +#else + if (file_exist('input.nml')) then + nml_unit = open_namelist_file() + ierr = 1 + do while (ierr /= 0) + read(nml_unit, nml=marine_strat_cloud_nml, iostat=io, end=10) + ierr = check_nml_error(io, 'marine_strat_cloud_nml') + enddo +10 call close_file(nml_unit) + endif +#endif + stdlog_unit = stdlog() + write(stdlog_unit, marine_strat_cloud_nml) + + call error_mesg(mod_name, 'The stratomulus diagnosis method is '// & + uppercase(trim(sc_diag_method)), NOTE) + + method_str = uppercase(trim(sc_diag_method)) + + if (method_str(1:3)=='EIS' .or. method_str(1:5)=='ECTEI' & + .or. method_str(1:4)=='PARK') then + id_eis = register_diag_field (mod_name, 'eis', axes(1:2), Time, & + 'estimated inversion strength', 'K') + end if + + if (method_str(1:5)=='ECTEI' .or. method_str(1:4)=='PARK') then + id_ectei = register_diag_field (mod_name, 'ectei', axes(1:2), Time, & + 'estimated cloud top entrainment index', 'K') + end if + + if (method_str(1:4)=='PARK') then + id_ELF = register_diag_field (mod_name, 'ELF', axes(1:2), Time, & + 'estimated low cloud fraction', '') + end if + + id_marine_strat = register_diag_field ( mod_name, 'marine_strat', axes(1:3), Time, & + 'marine low stratus cloud amount', '0-1' ) + + id_zlcl = register_diag_field (mod_name, 'zlcl', axes(1:2), Time, & + 'height of lcl', 'meter') + id_theta = register_diag_field (mod_name, 'theta', axes(1:3), Time, & + 'potential temperature', 'K') + id_lts = register_diag_field (mod_name, 'lts', axes(1:2), Time, & + 'low-tropospheric stability', 'K') + + if(intermediate_outputs_diags) then + id_dthdp = register_diag_field (mod_name, 'dthdp', axes(1:3), Time, & + 'dtheta/dp', 'K/hPa' ) + id_z700 = register_diag_field ( mod_name, 'z700', axes(1:2), Time, & + 'height of 700mb', 'meter') + + if (method_str(1:3)=='EIS') then + id_gamma_850 = register_diag_field (mod_name, 'gamma850', axes(1:2), Time, & + 'moist lapse rate at 850hPa', 'K/m') + end if + + if (method_str(1:4)=='PARK') then + id_beta1 = register_diag_field (mod_name, 'beta1', axes(1:2), Time, & + 'first low-level cloud suppression parameter', '') + id_beta2 = register_diag_field (mod_name, 'beta2', axes(1:2), Time, & + 'second low-level cloud suppression parameter', '') + id_zinv = register_diag_field (mod_name, 'zinv', axes(1:2), Time, & + 'height of invesion layer', 'meter') + id_DS = register_diag_field (mod_name, 'DS', axes(1:2), Time, & + 'decoupling strength', 'K') + id_IS = register_diag_field (mod_name, 'IS', axes(1:2), Time, & + 'inversion strength', 'K') + id_alpha = register_diag_field (mod_name, 'alpha', axes(1:2), Time, & + 'decoupling parameter', '') + id_low_cld_amt_park = register_diag_field ( mod_name, 'low_cld_amt_park', axes(1:2), Time, & + 'low cloud amount estimated from Park method', 'percent' ) + id_gamma_DL = register_diag_field (mod_name, 'gamma_DL', axes(1:2), Time, & + 'moist lapse rate at decoupling layer', 'K/m') + id_gamma_700 = register_diag_field (mod_name, 'gamma700', axes(1:2), Time, & + 'moist lapse rate at 700hPa', 'K/m') + end if + end if + + end subroutine marine_strat_cloud_init + + subroutine marine_strat_cloud_diag(temp, p_full, p_half, z_full, rh, q_hum, temp_2m, & + q_2m, rh_2m, psg, wg_full, klcls, cf, Time, ocean) + implicit none + real, intent(in), dimension(:,:,:) :: temp, q_hum, p_full, p_half, z_full, rh, wg_full + type(time_type), intent(in) :: Time + real, intent(in), dimension(:,:) :: temp_2m, q_2m, rh_2m, psg + integer, intent(in), dimension(:,:) :: klcls + logical, intent(in), dimension(:,:) :: ocean + real, intent(out), dimension(:,:,:) :: cf + + ! local variables + real, dimension(size(temp,1), size(temp,2), size(temp,3)) :: theta, dthdp, marine_strat + integer, dimension(size(temp,1), size(temp,2)) :: kdthdp, kinvs + real, dimension(size(temp,1), size(temp,2)) :: eis, ectei, ELF, low_ca_park + real :: strat, omega_pos_threshold + logical :: used + character(len=32) :: method_str = '' + integer :: i, j, k, k700, kb, k_surf, kk, nlev + + eis = 0.0 + ectei = 0.0 + ELF = 0.0 + dthdp = 0.0 + + call calc_theta_dthdp(temp, temp_2m, p_full, p_half, psg, theta, dthdp, kdthdp) + + method_str = uppercase(trim(sc_diag_method)) + if (method_str(1:3)=='EIS' .or. method_str(1:5)=='ECTEI') then + call calc_eis(p_full, z_full, temp, temp_2m, psg, klcls, eis, Time) + end if + if (method_str(1:5)=='ECTEI') then + call calc_ectei(p_full, q_hum, q_2m, eis, ectei, Time) + end if + if (method_str(1:4)=='PARK') then + call calc_Park_proxies(p_full, psg, z_full, temp, temp_2m, q_hum, & + q_2m, rh_2m, klcls, ELF, kinvs, Time) + end if + + k_surf = size(temp, 3) + omega_pos_threshold = 0. !1.4*100/3600 + marine_strat = 0.0 + + do i=1, size(temp, 1) + do j=1, size(temp, 2) + if (ocean(i,j)) then + ! =========== Add off-coast marine stratiform clouds =========== ! + kk = kdthdp(i,j) + + if (kk .ne. 0) then + kb = min(kk+1, k_surf) + do k = kk, kb + if (wg_full(i,j,k)>omega_pos_threshold .and. & + dthdp(i,j,k)8.0e4) then + call estimate_stratiform_cld(method_str, i, j, k, kb, p_full, & + cf, rh, theta, eis, dthdp, ectei, ELF) + marine_strat(i,j,k) = min(1.0, max(0.0, cf(i,j,k))) + end if + end do + endif + end if + end do + end do + + if (id_theta > 0) then + used = send_data(id_theta, theta, Time) + end if + if (id_marine_strat > 0) then + used = send_data(id_marine_strat, marine_strat, Time) + end if + + if(intermediate_outputs_diags) then + if (id_dthdp > 0) then + used = send_data(id_dthdp, dthdp, Time) + end if + if (id_low_cld_amt_park > 0) then + used = send_data(id_low_cld_amt_park, low_ca_park, Time) + end if + end if + end subroutine marine_strat_cloud_diag + + subroutine estimate_stratiform_cld(method_str, i, j, k, kb, pfull, & + cf, rh, theta, eis, dthdp, ectei, ELF) + implicit none + integer, intent(in) :: i, j, k + integer, intent(in) :: kb + character(len=32), intent(in) :: method_str + real, intent(in), dimension(:,:,:) :: rh, theta, pfull, dthdp + real, intent(in), dimension(:,:) :: eis, ectei, ELF + real, intent(out), dimension(:,:,:) :: cf + real :: strat, rhb_frac + integer :: k700, k_surf + + k_surf = size(pfull, 3) + k700 = minloc(abs(pfull(i,j,:) - 7.0e4), 1) + + if(method_str == 'LTS') then + strat = min(1.0, max(0.0, (theta(i,j,k700) - theta(i,j,k_surf)) * 0.057 - 0.5573)) + cf(i,j,k) = max(strat, cf(i,j,k)) + + else if(method_str == 'SLINGO') then + strat = min(1.0, max(0.0, -6.67*dthdp(i,j,k) - 0.667)) + rhb_frac = min(1.0, max(0.0, (rh(i,j,kb) - 0.6) / 0.2)) + cf(i,j,k) = min(1.0, max(cf(i,j,k), strat*rhb_frac)) + + else if(method_str == 'EIS_WOOD') then + !strat = min(1.0, max(0.0, 0.0221*eis(i,j) + 0.1128)) + strat = min(1.0, max(0.0, 0.06*eis(i,j) + 0.14)) ! Wood and Betherton, 2006 + cf(i,j,k) = min(1.0, max(cf(i,j,k), strat)) + + else if(method_str == 'ECTEI') then + ! Kawai, Koshiro and Webb, 2017 + strat = min(1.0, max(0.0, 0.031*ectei(i,j) + 0.39)) + cf(i,j,k) = min(1.0, max(cf(i,j,k), strat)) + + else if(method_str == 'PARK_ELF') then + ! Park and Shin, 2019, ACP + ! strat = min(1.0, max(0.0, 1.272*ELF(i,j)-0.366)) + strat = min(1.0, max(0.0, park_a * ELF(i,j) + park_b)) + cf(i,j,k) = min(1.0, max(cf(i,j,k), strat)) + + else + call error_mesg('cloud_simple', method_str//' is not supported yet!', FATAL) + + end if + end subroutine estimate_stratiform_cld + + subroutine calc_theta_dthdp(temp, temp_2m, pfull, phalf, ps, theta, dthdp, kdthdp) + real, intent(in), dimension(:,:,:) :: temp, pfull, phalf + real, intent(in), dimension(:,:) :: temp_2m, ps + real, intent(out), dimension(:,:,:) :: theta, dthdp + integer, intent(out), dimension(:,:) :: kdthdp + real, dimension(size(temp,1), size(temp,2)) :: theta_0 + real :: premib, pstar + integer :: i, j, k, kb + + kdthdp = 0 + premib = 8.0e4 + dthdp = 0.0 + pstar = 1.0e5 + + kb = size(temp, 3) !bottom level + do k=1,kb + theta(:,:,k) = temp(:,:,k) * (pstar / pfull(:,:,k))**(RDGAS / CP_AIR) + end do + + do k=1,kb-1 + dthdp(:,:,k) = (theta(:,:,k) - theta(:,:,k+1)) / (phalf(:,:,k) - phalf(:,:,k+1)) * 1.0e2 + end do + + theta_0 = temp_2m * (pstar / ps)**(RDGAS / CP_AIR) + dthdp(:,:,kb) = (theta(:,:,kb) - theta_0) / (phalf(:,:,kb) - ps) * 1.0e2 + + kdthdp = minloc(dthdp, dim=3, mask=(pfull>premib).and.(dthdp src/metpy/calc/basic.py + + implicit none + real, intent(in), dimension(:,:,:) :: geopot + real, dimension(size(geopot,1),size(geopot,2),size(geopot,3)):: height, scaled + + scaled = geopot * RADIUS + height = scaled * RADIUS / (GRAV_CONST * EARTH_MASS - scaled) + end function geopotential_to_height + + subroutine calc_lcls(klcls, pfull, temp, zfull, ts, ps, rh_surf, plcls, tlcls, zlcls) + ! Example to call: + ! call calc_lcls(klcls, pfull=p_full, plcls=plcl2d) + ! rh_surf in range [0,1] + implicit none + integer, intent(in), dimension(:,:) :: klcls + real, intent(in), dimension(:,:,:), optional :: temp, pfull, zfull + real, intent(in), dimension(:,:), optional :: rh_surf, ts, ps + real, intent(out), dimension(:,:), optional :: plcls, tlcls, zlcls + integer :: i, j + + do i=1, size(klcls,1) + do j=1, size(klcls,2) + + if(present(pfull) .and. present(plcls)) then + plcls(i,j) = pfull(i,j,klcls(i,j)) + end if + + if(present(temp) .and. present(tlcls)) then + tlcls(i,j) = temp(i,j,klcls(i,j)) + end if + + if (present(zfull) .and. present(zlcls)) then + zlcls(i,j) = zfull(i,j,klcls(i,j)) + end if + + if (present(rh_surf) .and. present(ts) .and. present(ps) .and. present(zlcls)) then + ! Use the exact LCL formula from D. M. Romps [2017, JAS 74(12)] + zlcls(i,j) = lcl(ps(i,j), ts(i,j), rh=rh_surf(i,j)) + end if + + if(.not.((present(pfull) .and. present(plcls)) .or. & + (present(temp) .and. present(tlcls)) .or. & + (present(zfull) .and. present(zlcls)) .or. & + (present(rh_surf) .and. present(ts) .and. present(ps) .and. present(zlcls)))) then + call error_mesg('calc_lcls in cloud_simple', 'At least one group of '// & + 'pfull(plcls), temp(tlcls) and zfull/rh_surf(zlcls) should exist.', FATAL) + end if + + end do + end do + + end subroutine calc_lcls + + subroutine calc_eis(pfull, zfull, temp, ts, ps, klcls, eis, Time) + ! Estimated inversion stability (EIS) + ! Refer to: Wood and Bretherton, 2006, Journal of Climate + implicit none + real, intent(in), dimension(:,:,:) :: pfull, zfull, temp + real, intent(in), dimension(:,:) :: ts, ps + integer, intent(in), dimension(:,:) :: klcls + type(time_type), intent(in) :: Time + real, intent(out), dimension(:,:) :: eis + real, dimension(size(temp,1), size(temp,2)) :: zlcl, z700, Gamma850, LTS + real, dimension(size(temp,1), size(temp,2), size(temp,3)) :: zfull_height + real :: pstar, T850 + logical :: used + integer :: k700, i, j + + zfull_height = geopotential_to_height(zfull*GRAV) + pstar = 1.e5 ! Pa + + do i=1, size(temp,1) + do j=1, size(temp,2) + k700 = minloc(abs(pfull(i,j,:) - 7.0e4), 1) + LTS(i,j) = temp(i,j,k700)*((pstar/pfull(i,j,k700))**(RDGAS/CP_AIR)) - & + ts(i,j)*(pstar/ps(i,j))**(RDGAS/CP_AIR) + T850 = (temp(i,j,k700) + ts(i,j)) / 2.0 + call calc_moist_lapse_rate(T850, 8.5e4, Gamma850(i,j)) + z700(i,j) = zfull_height(i,j,k700) + end do + end do + + call calc_lcls(klcls, zfull=zfull_height, zlcls=zlcl) + eis = LTS - Gamma850 * (z700 - zlcl) + + ! ----- output diagnositics ------ ! + if(id_eis > 0) then + used = send_data (id_eis, eis, Time) + end if + if(id_lts > 0) then + used = send_data (id_lts, LTS, Time) + end if + if(id_zlcl > 0) then + used = send_data (id_zlcl, zlcl, Time) + end if + + if(intermediate_outputs_diags) then + if(id_z700 > 0) then + used = send_data (id_z700, z700, Time) + end if + if(id_gamma_850 > 0) then + used = send_data (id_gamma_850, Gamma850, Time) + end if + end if + end subroutine calc_eis + + subroutine calc_ectei(pfull, q_hum, q_surf, eis, ectei, Time) + ! Estimated Cloud Top Entrainment Index (ECTEI) + ! Refer to: Eq(3) in Kawai, Koshiro and Webb, 2017, Journal of Climate + implicit none + real, intent(in), dimension(:,:,:) :: pfull, q_hum + real, intent(in), dimension(:,:) :: q_surf, eis + type(time_type), intent(in) :: Time + real, intent(out), dimension(:,:) :: ectei + real, dimension(size(pfull,1),size(pfull,2)) :: q_700 + integer :: k700, i, j + real :: k_en, C_qgap, beta + logical :: used + + k_en = 0.7 + C_qgap = 0.76 + beta = (1.0 - k_en) * C_qgap + + do i=1, size(pfull,1) + do j=1, size(pfull,2) + k700 = minloc(abs(pfull(i,j,:) - 7.0e4), 1) + q_700(i,j) = q_hum(i,j,k700) + end do + end do + + ectei = eis - beta * HLV / CP_AIR * (q_surf - q_700) + + if(id_ectei > 0) then + used = send_data (id_ectei, ectei, Time) + end if + end subroutine calc_ectei + + subroutine calc_Park_proxies(pfull, ps, zfull, temp, ts, q_hum, q_surf, & + rh_surf, klcls, ELF, kinvs, Time) + ! Refer to: Park and Shin, 2019, Atmospheric Chemistry and Physics + ! Heuristic estimation of low-level cloud fraction over the globe + ! based on a decoupling parameterization + ! https://www.atmos-chem-phys.net/19/5635/2019/ + + implicit none + real, intent(in), dimension(:,:,:) :: pfull, zfull, temp, q_hum + real, intent(in), dimension(:,:) :: ts, q_surf, ps, rh_surf + integer, intent(in), dimension(:,:) :: klcls + type(time_type), intent(in) :: Time + real, intent(out), dimension(:,:) :: ELF + integer, intent(out), dimension(:,:) :: kinvs + real, dimension(size(temp,1), size(temp,2)) :: plcl, tlcl, zlcl, z700, Gamma_DL, & + Gamma700, LTS, z_ML, zinv, qv_ML, beta2 + ! other paramters + real, dimension(size(temp,1), size(temp,2)) :: beta1, IS, DS, eis, ectei, alpha, f_para + real, dimension(size(temp,1), size(temp,2), size(temp,3)) :: zfull_height + real :: pstar, delta_zs, theta_ML + logical :: used + integer :: k700, i, j + + delta_zs = 2750.0 ! meter, constant + pstar = 1.0e5 ! Pa + kappa = RDGAS / CP_AIR + + zfull_height = geopotential_to_height(zfull*GRAV) + + call calc_lcls(klcls, pfull=pfull, temp=temp, ts=ts, ps=ps, & + rh_surf=rh_surf, plcls=plcl, tlcls=tlcl, zlcls=zlcl) + + where(zlcl < 0) + zlcl = 0.0 + end where + + do i=1, size(pfull,1) + do j=1, size(pfull,2) + k700 = minloc(abs(pfull(i,j,:) - 7.0e4), 1) + z700(i,j) = zfull_height(i,j,k700) + + ! Mixed Layer is the LCL + call calc_moist_lapse_rate(tlcl(i,j), plcl(i,j), Gamma_DL(i,j)) + call calc_moist_lapse_rate(temp(i,j,k700), pfull(i,j,k700), Gamma700(i,j)) + + theta_ML = ts(i,j) * (pstar / ps(i,j))**kappa + LTS(i,j) = temp(i,j,k700) * (pstar / pfull(i,j,k700))**kappa - theta_ML + qv_ML(i,j) = q_hum(i,j,klcls(i,j)) + end do + end do + + z_ML = zlcl + zinv = -LTS/Gamma700 + z700 + delta_zs*(Gamma_DL/Gamma700) + + ! Rest zinv + where(zinv < z_ML) + zinv = z_ML + end where + where(zinv > z_ML+delta_zs) + zinv = z_ML + delta_zs + end where + + do i=1, size(pfull,1) + do j=1, size(pfull,2) + kinvs(i,j) = minloc(abs(zinv(i,j)-zfull_height(i,j,:)), 1) + end do + end do + + ! low-level cloud suppression parameters (LCS) + beta2 = sqrt(zinv*zlcl) / delta_zs + ! freeze-dry factor (Vavrus and Waliser, 2008) + f_para = max(0.15, min(1.0, qv_ML/0.003)) + ! Estimated low-cloud fraction (ELF) + ELF = f_para * (1.0 - beta2) + + ! ----- output diagnostics ----- ! + if(id_ELF>0) then + used = send_data(id_ELF, ELF, Time) + end if + if(id_lts>0) then + used = send_data(id_lts, LTS, Time) + end if + if(id_zlcl>0) then + used = send_data(id_zlcl, zlcl, Time) + end if + + if(intermediate_outputs_diags) then + !============= Other prameters =============! + beta1 = (zinv + zlcl) / delta_zs + alpha = (zinv - z_ML) / delta_zs + IS = (1.0 - alpha) * Gamma_DL * delta_zs + DS = alpha * Gamma_DL * delta_zs + eis = LTS + Gamma_DL*z_ML - Gamma700*z700 + call calc_ectei(pfull, q_hum, q_surf, eis, ectei, Time) + + !Add some diagnostic ouputs + call output_extra_diags_for_Park_ELF(Time, beta1, beta2, & + alpha, eis, IS, DS, z700, zinv, Gamma700, Gamma_DL) + end if + end subroutine calc_Park_proxies + + subroutine calc_moist_lapse_rate(T, p, Gamma) + real, intent(in) :: T, p + real, intent(out) :: Gamma + real :: es, qs + + ! Eq(5) in the following paper: + ! Wood & Bretherton (2006). On the relationship between stratiform low cloud + ! cover and lower-tropospheric stability. Journal of climate, 19(24), 6425-6432. + + call lookup_es(T, es) + qs = 0.622 * es / (p - es) + Gamma = (GRAV/CP_AIR) * (1.0 - (1.0 + HLV*qs/RDGAS/T) / (1.0 + HLV**2 * qs/CP_AIR/RVGAS/T**2)) + end subroutine calc_moist_lapse_rate + + subroutine output_extra_diags_for_Park_ELF(Time, beta1, beta2, & + alpha, eis, IS, DS, z700, zinv, Gamma700, Gamma_DL) + + real, intent(in), dimension(:,:) :: beta1, beta2, alpha, eis, & + IS, DS, z700, zinv, Gamma700, Gamma_DL + type(time_type) , intent(in) :: Time + logical :: used + + if (id_eis>0) then + ! Notice the eis here is a little different from that in calc_eis + used = send_data (id_eis, eis, Time) + endif + if (id_beta1 > 0) then + used = send_data (id_beta1, beta1, Time) + endif + if (id_beta2 > 0) then + used = send_data (id_beta2, beta2, Time) + endif + if (id_alpha > 0) then + used = send_data (id_alpha, alpha, Time) + endif + if (id_DS > 0) then + used = send_data(id_DS, DS, Time) + endif + if (id_IS > 0) then + used = send_data (id_IS, IS, Time) + endif + if (id_zinv > 0) then + used = send_data (id_zinv, zinv, Time) + endif + if (id_z700 > 0) then + used = send_data (id_z700, z700, Time) + endif + if (id_gamma_700 > 0) then + used = send_data (id_gamma_700, Gamma700, Time) + endif + if (id_gamma_DL > 0) then + used = send_data (id_gamma_DL, Gamma_DL, Time) + endif + end subroutine output_extra_diags_for_Park_ELF + + subroutine marine_strat_cloud_end() + + end subroutine marine_strat_cloud_end + +end module marine_strat_cloud_mod diff --git a/src/atmos_param/diffusivity/diffusivity.F90 b/src/atmos_param/diffusivity/diffusivity.F90 index c9a44410e..921e5bb73 100644 --- a/src/atmos_param/diffusivity/diffusivity.F90 +++ b/src/atmos_param/diffusivity/diffusivity.F90 @@ -261,7 +261,7 @@ end subroutine diffusivity_end !======================================================================= subroutine diffusivity(t, q, u, v, p_full, p_half, z_full, z_half, & - u_star, b_star, h, k_m, k_t, kbot) + u_star, b_star, h, k_m, k_t, ind_lcl, kbot) real, intent(in), dimension(:,:,:) :: t, q, u, v real, intent(in), dimension(:,:,:) :: p_full, p_half @@ -269,6 +269,7 @@ subroutine diffusivity(t, q, u, v, p_full, p_half, z_full, z_half, & real, intent(in), dimension(:,:) :: u_star, b_star real, intent(inout), dimension(:,:,:) :: k_m, k_t real, intent(out), dimension(:,:) :: h +integer, intent(in), optional, dimension(:,:) :: ind_lcl integer, intent(in), optional, dimension(:,:) :: kbot real, dimension(size(t,1),size(t,2),size(t,3)) :: svcp,z_full_ag, & @@ -311,9 +312,16 @@ subroutine diffusivity(t, q, u, v, p_full, p_half, z_full, z_half, & end do z_half_ag(:,:,nlev+1) = z_half(:,:,nlev+1) - z_surf(:,:) - if(fixed_depth) then h = depth_0 +else if (present(ind_lcl)) then + nlat = size(t,2) + nlon = size(t,1) + do j=1,nlat + do i=1,nlon + h(i,j)=z_full_ag(i,j,ind_lcl(i,j)) + end do + end do else call pbl_depth(svcp,u,v,z_full_ag,u_star,b_star,h,kbot=kbot) end if diff --git a/src/atmos_param/dry_convection/dry_convection.f90 b/src/atmos_param/dry_convection/dry_convection.f90 index 36d8fee19..b113fa998 100644 --- a/src/atmos_param/dry_convection/dry_convection.f90 +++ b/src/atmos_param/dry_convection/dry_convection.f90 @@ -117,7 +117,7 @@ end subroutine dry_convection_init !! @param[out] dt_tg Calculated temperature tendency !! @param[out] cape Convective Available Potential Energy !! @param[out] cin Convective Inhibition - subroutine dry_convection(Time, tg, p_full, p_half, dt_tg, cape, cin) + subroutine dry_convection(Time, tg, p_full, p_half, dt_tg, cape, cin, lzb, lcl) type(time_type), intent(in) :: Time @@ -131,6 +131,10 @@ subroutine dry_convection(Time, tg, p_full, p_half, dt_tg, cape, cin) real, intent(out), dimension(size(tg,1),size(tg,2)) :: & cape, & !< convectively available potential energy cin !< convective inhibition + integer, intent(out), dimension(:,:) :: & + lcl, & !< lifting condensation level (index) + lzb !< level of zero buoyancy + ! --- local variables --- real, dimension(size(tg,1),size(tg,2)) :: & @@ -138,8 +142,6 @@ subroutine dry_convection(Time, tg, p_full, p_half, dt_tg, cape, cin) ener_int !< energy integral from ground to LZB integer, dimension(size(tg,1),size(tg,2)) :: & - lcl, & !< lifting condensation level (index) - lzb, & !< level of zero buoyancy btm !< bottom of convecting region real, dimension(size(tg,1),size(tg,2), size(tg,3)) :: & @@ -174,7 +176,7 @@ subroutine dry_convection(Time, tg, p_full, p_half, dt_tg, cape, cin) do i=1, size(tg,1) do j=1, size(tg,2) - + do k=1, num_levels if(k>=lzb(i,j).and. k<=btm(i,j)) then ! in convecting region between ground and LZB diff --git a/src/atmos_param/hs_forcing/hs_forcing.F90 b/src/atmos_param/hs_forcing/hs_forcing.F90 index cd0566d42..70fc3d23c 100644 --- a/src/atmos_param/hs_forcing/hs_forcing.F90 +++ b/src/atmos_param/hs_forcing/hs_forcing.F90 @@ -49,7 +49,11 @@ module hs_forcing_mod CONSTANT, INTERP_WEIGHTED_P use astronomy_mod, only: diurnal_exoplanet, astronomy_init, obliq, ecc +#ifdef COLUMN_MODEL +use spec_mpp_mod, only: grid_domain, get_grid_domain +#else use transforms_mod, only: grid_domain, get_grid_domain +#endif implicit none @@ -985,7 +989,7 @@ subroutine top_down_newtonian_damping ( Time, lat, ps, p_full, p_half, t, tdt, t teq(:,:,k) = max(teq(:,:,k), tstr(:,:)) elseif (stratosphere_t_option == 'extend_tp') then do i=1,size(t,1) - do j=1,size(t,1) + do j=1,size(t,2) if (zfull(i,j,k)/1000 >= h_trop(i,j)) then teq(i,j,k) = t_trop(i,j) endif diff --git a/src/atmos_param/qe_moist_convection/qe_moist_convection.F90 b/src/atmos_param/qe_moist_convection/qe_moist_convection.F90 index af299fcfa..2f57b9ecf 100644 --- a/src/atmos_param/qe_moist_convection/qe_moist_convection.F90 +++ b/src/atmos_param/qe_moist_convection/qe_moist_convection.F90 @@ -189,7 +189,7 @@ end subroutine generate_lcl_table subroutine qe_moist_convection (dt, Tin, qin, p_full, p_half, coldT, & rain, snow, deltaT, deltaq, qref, convflag, & kLZBs, CAPE, CIN, invtau_q_relaxation, & - invtau_t_relaxation, Tref) + invtau_t_relaxation, Tref, kLCLs) !----------------------------------------------------------------------- ! @@ -231,9 +231,9 @@ subroutine qe_moist_convection (dt, Tin, qin, p_full, p_half, coldT, & real , intent(in) :: dt logical, intent(in) , dimension(:,:) :: coldT - real , intent(out), dimension(:,:) :: rain, snow, kLZBs, CAPE, CIN + real , intent(out), dimension(:,:) :: rain, snow, CAPE, CIN real , intent(out), dimension(:,:) :: invtau_q_relaxation, invtau_t_relaxation - integer, intent(out), dimension(:,:) :: convflag + integer, intent(out), dimension(:,:) :: convflag, kLZBs, kLCLs real , intent(out), dimension(:,:,:) :: deltaT, deltaq, qref, Tref !----------------------------------------------------------------------- @@ -248,7 +248,7 @@ subroutine qe_moist_convection (dt, Tin, qin, p_full, p_half, coldT, & call SBM_convection_scheme(dt, Tin, qin, p_full, p_half, rain, snow, & deltaT, deltaq, kLZBs, CAPE, CIN,invtau_q_relaxation, & invtau_t_relaxation, Tref, qref, & - val_min, val_max, val_inc, lcl_temp_table, convflag) + val_min, val_max, val_inc, lcl_temp_table, convflag, kLCLs) end subroutine qe_moist_convection @@ -256,7 +256,8 @@ end subroutine qe_moist_convection subroutine SBM_convection_scheme(dt, Tin, qin, p_full, p_half, rain, snow, & deltaT, deltaq, kLZBs, CAPE, CIN, invtau_q_relaxation, invtau_t_relaxation,& - Tref, qref, val_min, val_max, val_inc, lcl_temp_table, convflag) + Tref, qref, val_min, val_max, val_inc, lcl_temp_table, convflag, kLCLs) + !----------------------------------------------------------------------- ! @@ -275,11 +276,10 @@ subroutine SBM_convection_scheme(dt, Tin, qin, p_full, p_half, rain, snow, & real, intent(in), dimension(:) :: lcl_temp_table real, intent(out), dimension(:,:) :: rain, snow, CAPE, CIN real, intent(out), dimension(:,:) :: invtau_q_relaxation, invtau_t_relaxation - real, intent(out), dimension(:,:) :: kLZBs real, intent(out), dimension(:,:,:) :: deltaT, deltaq, Tref, qref - integer, intent(out), dimension(:,:) :: convflag + integer, intent(out), dimension(:,:) :: kLZBs, convflag, kLCLs - integer :: k_surface, i, j, kLZB + integer :: k_surface, i, j, kLZB, kLCL real, dimension(size(Tin, 3)) :: & deltaq_parcel, deltaT_parcel, T_parcel, r_parcel, qref_parcel, Tref_parcel real, dimension(size(Tin, 1), size(Tin, 2)) :: Pq @@ -287,9 +287,6 @@ subroutine SBM_convection_scheme(dt, Tin, qin, p_full, p_half, rain, snow, & real :: cape_parcel, cin_parcel, Pq_parcel, Pt_parcel real :: invtau_q_relaxation_parcel, invtau_t_relaxation_parcel - - - ! Initialization of parameters and variables k_surface = size(Tin, 3) deltaq = 0. @@ -318,12 +315,14 @@ subroutine SBM_convection_scheme(dt, Tin, qin, p_full, p_half, rain, snow, & ! parcel lifted from lowest model level call CAPE_calculation(k_surface, p_full(i,j,:), p_half(i,j,:), & Tin(i,j,:), rin(i,j,:), kLZB, T_parcel, r_parcel, & - cape_parcel, cin_parcel, val_min, val_max, lcl_temp_table) + cape_parcel, cin_parcel, val_min, val_max, lcl_temp_table,& + kLCL) ! Store values CAPE(i,j) = cape_parcel CIN(i,j) = cin_parcel kLZBs(i,j) = kLZB + kLCLs(i,j) = kLCL ! If CAPE>0, set reference temperature and humidity above and below ! the LZB (Level of Zero Buoyancy) @@ -394,7 +393,8 @@ end subroutine sbm_convection_scheme !####################################################################### subroutine CAPE_calculation(k_surface, p_full, p_half, Tin, rin, kLZB, & - Tp, rp, CAPE, CIN, val_min, val_max, lcl_temp_table) + Tp, rp, CAPE, CIN, val_min, val_max, lcl_temp_table, kLCL) + ! Calculates CAPE, CIN, level of zero buoyancy, and parcel properties ! (second order accurate in delta(ln p) and exact LCL calculation) @@ -405,13 +405,13 @@ subroutine CAPE_calculation(k_surface, p_full, p_half, Tin, rin, kLZB, & real, intent(in), dimension(:) :: Tin, rin real, intent(in) :: val_min , val_max real, intent(in), dimension(:) :: lcl_temp_table - integer, intent(out) :: kLZB + integer, intent(out) :: kLZB, kLCL real, intent(out), dimension(:) :: Tp, rp real, intent(out) :: CAPE, CIN logical :: nocape, saturated, skip real :: pLZB, T0, r0, es, rs, pLCL - integer :: kLFC, k, kLCL + integer :: kLFC, k real, dimension(size(Tin)) :: Tin_virtual nocape = .true. @@ -423,6 +423,7 @@ subroutine CAPE_calculation(k_surface, p_full, p_half, Tin, rin, kLZB, & Tp = Tin rp = rin saturated = .false. + kLCL = 0 ! Calculation of values to check whether the lowest level is saturated ! Calculate the virtual temperature @@ -452,6 +453,7 @@ subroutine CAPE_calculation(k_surface, p_full, p_half, Tin, rin, kLZB, & ! Calculation above the LCL call CAPE_above_LCL(kLCL, kLZB, kLFC, Tp, rp, rin, p_full, nocape, skip,& CIN, CAPE, Tin, Tin_virtual, p_half, pLZB) + end subroutine CAPE_CALCULATION diff --git a/src/atmos_param/ras/ras.f90 b/src/atmos_param/ras/ras.f90 index 6fb228041..a4bc63a2d 100644 --- a/src/atmos_param/ras/ras.f90 +++ b/src/atmos_param/ras/ras.f90 @@ -492,6 +492,7 @@ SUBROUTINE RAS( is, js, Time, temp0, qvap0, & uwnd0, vwnd0, pres0, pres0_int, zhalf0, coldT0, & dtime, dtemp0, dqvap0, duwnd0, dvwnd0, & rain0, snow0, do_strat, & + klzbs, klcls, & !OPTIONAL IN mask, kbot, & !OPTIONAL OUT @@ -554,6 +555,7 @@ SUBROUTINE RAS( is, js, Time, temp0, qvap0, & ! Da0 - OPTIONAL; cloud fraction change ! DR0 - OPTIONAL; increment to prognostic tracers !--------------------------------------------------------------------- + integer, intent(out), dimension(:,:) :: klzbs, klcls real, intent(out), dimension(:,:,:) :: dtemp0, dqvap0, duwnd0, dvwnd0 real, intent(out), dimension(:,:) :: rain0, snow0 @@ -569,7 +571,7 @@ SUBROUTINE RAS( is, js, Time, temp0, qvap0, & logical :: coldT, exist real :: precip, Hl, psfc, dpcu, dtinv - integer :: ksfc, klcl + integer :: ksfc, klcl, klzb integer, dimension(SIZE(temp0,3)) :: ic @@ -750,6 +752,12 @@ SUBROUTINE RAS( is, js, Time, temp0, qvap0, & end do end if + do j = 1,jmax + do i = 1,imax + klcls(i,j) = kcbase(i,j) + end do + end do + !--------------------------------------------------------------------- ! LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL @@ -853,6 +861,7 @@ SUBROUTINE RAS( is, js, Time, temp0, qvap0, & Hl, exist ) if ( .not. exist ) CYCLE + klzbs(i,j) = klcl !--------------------------------------------------------------------- ! Cloud top loop starts !--------------------------------------------------------------------- @@ -862,6 +871,8 @@ SUBROUTINE RAS( is, js, Time, temp0, qvap0, & ib = ic(nc) if( ib >= klcl) CYCLE + klzbs(i,j) = min(klzbs(i,j), ib) + if ( setras ) then ! --- Compute some stuff alpha(:) = qvap_sat(:) - dqvap_sat(:) * temp(:) diff --git a/src/atmos_param/rayleigh_bottom_drag/rayleigh_bottom_drag.F90 b/src/atmos_param/rayleigh_bottom_drag/rayleigh_bottom_drag.F90 index 785f43d0b..73e2f283a 100644 --- a/src/atmos_param/rayleigh_bottom_drag/rayleigh_bottom_drag.F90 +++ b/src/atmos_param/rayleigh_bottom_drag/rayleigh_bottom_drag.F90 @@ -124,27 +124,22 @@ subroutine compute_rayleigh_bottom_drag( is, ie, js, je, & real, intent(in) :: delta_t real, intent(in), dimension (:,:) :: lat - real, optional, dimension (:,:,:) :: ug_previous, vg_previous, & + real, intent(in), dimension (:,:,:) :: ug_previous, vg_previous, & p_half_previous, p_full_previous real, intent(inout), dimension(:,:,:) :: dt_ug, dt_vg, dt_tg real, intent(out), dimension(:,:,:) :: dissipative_heat - if(present(ug_previous)) then - if (do_drag_at_surface) then - call surface_drag(Time, delta_t, lat, is, js, ug_previous, vg_previous, & - p_half_previous, p_full_previous, dt_ug, dt_vg, & - dt_tg, dissipative_heat) - else - call interior_drag(Time, delta_t, lat, is, js, ug_previous, vg_previous, & - p_half_previous, p_full_previous, dt_ug, dt_vg, & - dt_tg, dissipative_heat) - endif - else - call error_mesg('compute_rayleigh_bottom_drag', & - 'ug_previous is not present',fatal) - end if + if (do_drag_at_surface) then + call surface_drag(Time, delta_t, lat, is, js, ug_previous, vg_previous, & + p_half_previous, p_full_previous, dt_ug, dt_vg, & + dt_tg, dissipative_heat) + else + call interior_drag(Time, delta_t, lat, is, js, ug_previous, vg_previous, & + p_half_previous, p_full_previous, dt_ug, dt_vg, & + dt_tg, dissipative_heat) + endif end subroutine compute_rayleigh_bottom_drag @@ -158,9 +153,9 @@ subroutine surface_drag(Time, delta_t, lat, is, js, ug, vg, p_half, p_full, & integer, intent(in) :: is, js real, intent(in), dimension (:,:,:) :: ug, vg, p_full, p_half real, intent(inout), dimension(:,:,:) :: dt_ug, dt_vg, dt_tg - real, dimension(size(ug,1),size(ug,2)) :: sigma, sigma_norm, sigma_max real, intent(out), dimension(:,:,:) :: dissipative_heat - + + real, dimension(size(ug,1),size(ug,2)) :: sigma, sigma_norm, sigma_max real, dimension(size(ug,1),size(ug,2),size(ug,3)):: dt_u_temp, dt_v_temp real, dimension(size(ug,2)) :: drag_coeff integer :: j, k, num_level @@ -234,9 +229,9 @@ subroutine interior_drag(Time, delta_t, lat, is, js, ug, vg, p_half, p_full, & integer, intent(in) :: is, js real, intent(in), dimension (:,:,:) :: ug, vg, p_full, p_half real, intent(inout), dimension(:,:,:) :: dt_ug, dt_vg, dt_tg - real, dimension(size(ug,1),size(ug,2)) :: sigma, sigma_norm, sigma_max real, intent(out), dimension(:,:,:) :: dissipative_heat - + + real, dimension(size(ug,1),size(ug,2)) :: sigma, sigma_norm, sigma_max real, dimension(size(ug,1),size(ug,2),size(ug,3)):: dt_u_temp, dt_v_temp real, dimension(size(ug,2)) :: drag_coeff integer :: j, k, num_level diff --git a/src/atmos_param/rrtm_radiation/rrtm_radiation.f90 b/src/atmos_param/rrtm_radiation/rrtm_radiation.F90 similarity index 96% rename from src/atmos_param/rrtm_radiation/rrtm_radiation.f90 rename to src/atmos_param/rrtm_radiation/rrtm_radiation.F90 index 3f498b402..6b80f7789 100644 --- a/src/atmos_param/rrtm_radiation/rrtm_radiation.f90 +++ b/src/atmos_param/rrtm_radiation/rrtm_radiation.F90 @@ -131,6 +131,9 @@ module rrtm_vars character(len=256) :: co2_file='co2' ! file name of co2 file to read character(len=256) :: co2_variable_name='co2' ! field name of co2 file to read + logical :: do_scm_ozone=.false. ! read single column ozone from namelist? note: ONLY when using SCM. + real(kind=rb), dimension(100) :: scm_ozone = -1 ! input array for single column ozone. max number of levels = 100 + ! secondary gases (CH4,N2O,O2,CFC11,CFC12,CFC22,CCL4) logical :: include_secondary_gases=.false. ! non-zero values for above listed secondary gases? real(kind=rb) :: ch4_val = 0. ! if .true., value for CH4 vmr @@ -210,7 +213,7 @@ module rrtm_vars &lonstep, do_zm_tracers, do_zm_rad, & &do_precip_albedo, precip_albedo_mode, precip_albedo, precip_lat,& &do_read_co2, co2_file, co2_variable_name, use_dyofyr, solrad, & - &solday, equinox_day,solr_cnst + &solday, equinox_day,solr_cnst, do_scm_ozone, scm_ozone end module rrtm_vars !***************************************************************************************** @@ -240,7 +243,11 @@ subroutine rrtm_radiation_init(axes,Time,ncols,nlay,lonb,latb, Time_step) &write_version_number, stdlog, & &error_mesg, NOTE, WARNING, FATAL use time_manager_mod, only: time_type, length_of_day, get_time - use transforms_mod, only: get_grid_domain +#ifdef COLUMN_MODEL + use spec_mpp_mod, only: get_grid_domain +#else + use transforms_mod, only: get_grid_domain +#endif ! Local variables implicit none @@ -445,7 +452,7 @@ subroutine rrtm_radiation_init(axes,Time,ncols,nlay,lonb,latb, Time_step) if(do_read_ozone)then call interpolator_init (o3_interp, trim(ozone_file)//'.nc', lonb, latb, data_out_of_bounds=(/ZERO/)) - endif + endif if(do_read_h2o)then call interpolator_init (h2o_interp, trim(h2o_file)//'.nc', lonb, latb, data_out_of_bounds=(/ZERO/)) @@ -455,6 +462,12 @@ subroutine rrtm_radiation_init(axes,Time,ncols,nlay,lonb,latb, Time_step) call interpolator_init (co2_interp, trim(co2_file)//'.nc', lonb, latb, data_out_of_bounds=(/ZERO/)) endif + if(do_scm_ozone)then + call error_mesg('run_rrtm', & + 'Input o3 will be read in exactly as specified in input (i.e. no plevel interpolation will be performed). Ensure it is specified correctly in namelist. ONLY FOR USE IN SINGLE COLUMN MODEL.', & + WARNING) + endif + if(store_intermediate_rad .or. id_flux_sw > 0) & allocate(sw_flux(size(lonb,1)-1,size(latb,2)-1)) if(store_intermediate_rad .or. id_flux_lw > 0) & @@ -549,7 +562,11 @@ subroutine run_rrtmg(is,js,Time,lat,lon,p_full,p_half,albedo,q,t,t_surf_rad,tdt, use diag_manager_mod, only: register_diag_field, send_data use time_manager_mod,only: time_type +#ifdef COLUMN_MODEL + use column_grid_mod, only: area_weighted_global_mean +#else use transforms_mod,only: area_weighted_global_mean +#endif !--------------------------------------------------------------------------------------------------------------- ! In/Out variables implicit none @@ -648,7 +665,7 @@ subroutine run_rrtmg(is,js,Time,lat,lon,p_full,p_half,albedo,q,t,t_surf_rad,tdt, !!!!! mp586 addition for annual mean insolation !!!!! !!!! following https://github.com/sit23/Isca/blob/master/src/atmos_param/socrates/interface/socrates_interface.F90#L888 !!!! - if (frierson_solar_rad .eq. .true.) then + if (frierson_solar_rad) then p2 = (1. - 3.*sin(lat(:,:))**2)/4. coszen = 0.25 * (1.0 + del_sol * p2 + del_sw * sin(lat(:,:))) rrsun = 1 ! needs to be set, set to 1 so that stellar_radiation is unchanged in socrates_interface @@ -725,11 +742,30 @@ subroutine run_rrtmg(is,js,Time,lat,lon,p_full,p_half,albedo,q,t,t_surf_rad,tdt, !get ozone if(do_read_ozone)then call interpolator( o3_interp, Time_loc, p_half, o3f, trim(ozone_file)) - if (input_o3_file_is_mmr==.true.) then + endif + if(do_scm_ozone)then ! Allows for option to specify ozone vertical profile in namelist for SCM. + if(do_read_ozone)then + call error_mesg('run_rrtm', 'Cannot set do_scm_ozone and do_read_ozone = .true.', FATAL) + endif + if((size(q,1)>1).or.(size(q,2)>1))then + call error_mesg('run_rrtm', 'Cannot set do_scm_ozone if simulating more than one column, use do_read_ozone instead', FATAL) + endif + if(scm_ozone(size(q,3)).eq.-1)then + call error_mesg('run_rrtm', 'Input o3 must be specified on model pressure levels but not enough levels specified', FATAL) + endif + if(scm_ozone(size(q,3)+1).ne.-1)then + call error_mesg('run_rrtm', 'Input o3 must be specified on model pressure levels but too many levels specified', FATAL) + endif + o3f(1,1,:) = scm_ozone(1:size(q,3)) + !PUT THIS WARNING SOMEWHERE ELSE + endif + if (do_read_ozone .or. do_scm_ozone) then + if (input_o3_file_is_mmr) then o3f = o3f * (1000. * gas_constant / rdgas ) / wtmozone !RRTM expects all abundances to be volume mixing ratio. So if input file is mass mixing ratio, it must be converted to volume mixing ratio using the molar masses of dry air and ozone. ! Molar mass of dry air calculated from gas_constant / rdgas, and converted into g/mol from kg/mol by multiplying by 1000. This conversion is necessary because wtmozone is in g/mol. endif endif + !get co2 if(do_read_co2)then @@ -800,8 +836,8 @@ subroutine run_rrtmg(is,js,Time,lat,lon,p_full,p_half,albedo,q,t,t_surf_rad,tdt, &phalf(:,sk+1) = pfull(:,sk)*0.5 tfull = reshape(t (1:si:lonstep,:,sk :1:-1),(/ si*sj/lonstep,sk /)) thalf = reshape(t_half(1:si:lonstep,:,sk+1:1:-1),(/ si*sj/lonstep,sk+1 /)) - h2o = reshape(h2o_vmr (1:si:lonstep,:,sk :1:-1),(/ si*sj/lonstep,sk /)) - if(do_read_ozone)o3 = reshape(o3f(1:si:lonstep,:,sk :1:-1),(/ si*sj/lonstep,sk /)) + h2o = reshape(h2o_vmr(1:si:lonstep,:,sk :1:-1),(/ si*sj/lonstep,sk /)) + if((do_read_ozone).or.(do_scm_ozone))o3 = reshape(o3f(1:si:lonstep,:,sk :1:-1),(/ si*sj/lonstep,sk /)) if(do_read_co2)co2 = reshape(co2f(1:si:lonstep,:,sk :1:-1),(/ si*sj/lonstep,sk /)) diff --git a/src/atmos_param/socrates/interface/read_control.F90 b/src/atmos_param/socrates/interface/read_control.F90 index 5ed7fae81..c877f9fbe 100644 --- a/src/atmos_param/socrates/interface/read_control.F90 +++ b/src/atmos_param/socrates/interface/read_control.F90 @@ -9,13 +9,14 @@ MODULE read_control_mod ! Subroutine to set input algorithmic options for the core radiation code !------------------------------------------------------------------------------ -SUBROUTINE read_control(control, spectrum) - +SUBROUTINE read_control(control, spectrum, do_clouds) USE rad_pcf USE def_control, ONLY: StrCtrl, allocate_control USE def_spectrum, ONLY: StrSpecData -USE socrates_config_mod, ONLY: l_planet_grey_surface, inc_h2o, inc_co2, inc_co, inc_o3, inc_n2o, inc_ch4, inc_o2, inc_so2, inc_cfc11, inc_cfc12, inc_cfc113, inc_hcfc22, inc_hfc134a +USE socrates_config_mod, ONLY: l_planet_grey_surface, inc_h2o, inc_co2, inc_co, & + inc_o3, inc_n2o, inc_ch4, inc_o2, inc_so2, inc_cfc11, & + inc_cfc12, inc_cfc113, inc_hcfc22, inc_hfc134a IMPLICIT NONE @@ -26,6 +27,9 @@ SUBROUTINE read_control(control, spectrum) ! Spectral data: TYPE (StrSpecData), INTENT(IN) :: spectrum +LOGICAL, INTENT(IN), OPTIONAL :: do_clouds + + ! Local variables. INTEGER :: i ! Loop variable @@ -93,7 +97,11 @@ SUBROUTINE read_control(control, spectrum) control%i_gas_overlap = ip_overlap_k_eqv_scl ! Properties of clouds -control%i_cloud_representation = ip_cloud_off +if (do_clouds) then + control%i_cloud_representation = ip_cloud_ice_water +else + control%i_cloud_representation = ip_cloud_off +end if control%i_overlap = ip_max_rand control%i_inhom = ip_homogeneous diff --git a/src/atmos_param/socrates/interface/set_dimen.F90 b/src/atmos_param/socrates/interface/set_dimen.F90 index 4f62eb3f9..3301bbbbd 100644 --- a/src/atmos_param/socrates/interface/set_dimen.F90 +++ b/src/atmos_param/socrates/interface/set_dimen.F90 @@ -61,14 +61,17 @@ SUBROUTINE set_dimen(control, dimen, spectrum, n_profile, n_layer, & dimen%nd_subcol_gen = cld_subcol_gen dimen%nd_subcol_req = cld_subcol_req -dimen%id_cloud_top = dimen%nd_layer + 1 - n_cloud_layer +dimen%id_cloud_top = dimen%nd_layer + 1 - n_cloud_layer !ie TOA + IF (control%l_cloud) THEN - dimen%nd_layer_clr = dimen%id_cloud_top - 1 + ! this gives the allocate the full column for radiation + ! rather than the layers over which cloud is present + dimen%nd_layer_clr = dimen%nd_layer + ELSE dimen%nd_layer_clr = dimen%nd_layer END IF - ! Aerosol dimen%nd_aerosol_mode = MAX(1,n_aer_mode) diff --git a/src/atmos_param/socrates/interface/socrates_calc.F90 b/src/atmos_param/socrates/interface/socrates_calc.F90 index 6a07a8d3b..bc40be70f 100644 --- a/src/atmos_param/socrates/interface/socrates_calc.F90 +++ b/src/atmos_param/socrates/interface/socrates_calc.F90 @@ -17,15 +17,12 @@ module socrates_calc_mod contains -! ================================================================================== - - - +! ============================================================================== ! Set up the call to the Socrates radiation scheme ! ----------------------------------------------------------------------------- !DIAG Added Time -subroutine socrates_calc(Time_diag,control, spectrum, & +subroutine socrates_calc(Time_diag,control, spectrum, & n_profile, n_layer, n_cloud_layer, n_aer_mode, & cld_subcol_gen, cld_subcol_req, & p_layer, t_layer, t_layer_boundaries, d_mass, density, & @@ -33,7 +30,10 @@ subroutine socrates_calc(Time_diag,control, spectrum, t_rad_surf, cos_zenith_angle, solar_irrad, orog_corr, & l_planet_grey_surface, planet_albedo, planet_emissivity, & layer_heat_capacity, & - flux_direct, flux_down, flux_up, heating_rate, spectral_olr) + cld_frac, reff_rad, mmr_cl_rad, & + flux_direct, flux_down, flux_up, & + flux_direct_clear, flux_down_clear, flux_up_clear, & + heating_rate, spectral_olr, tot_cloud_cover) use rad_pcf use def_control, only: StrCtrl @@ -45,12 +45,12 @@ subroutine socrates_calc(Time_diag,control, spectrum, use def_aer, only: StrAer, deallocate_aer, deallocate_aer_prsc use def_out, only: StrOut, deallocate_out -use set_control_mod, only: set_control -use set_dimen_mod, only: set_dimen -use set_atm_mod, only: set_atm -use set_bound_mod, only: set_bound -use set_cld_mod, only: set_cld -use set_aer_mod, only: set_aer +use set_control_mod, only: set_control +use set_dimen_mod, only: set_dimen +use set_atm_mod, only: set_atm +use set_bound_mod, only: set_bound +use socrates_set_cld, only: set_cld +use set_aer_mod, only: set_aer use soc_constants_mod, only: i_def, r_def @@ -114,17 +114,34 @@ subroutine socrates_calc(Time_diag,control, spectrum, real(r_def), intent(in) :: layer_heat_capacity(n_profile, n_layer) ! Heat capacity of layer +real(r_def), intent(in) :: cld_frac(n_profile, n_layer) +! Cloud fraction at layer centres + +real(r_def), intent(in) :: reff_rad(n_profile, n_layer) +! Cloud liquid particle radius from simple cloud scheme + +real(r_def), intent(in) :: mmr_cl_rad(n_profile, n_layer) +! Cloud liquid mmr at layer centres + real(r_def), intent(out) :: flux_direct(n_profile, 0:n_layer) ! Direct (unscattered) downwards flux (Wm-2) real(r_def), intent(out) :: flux_down(n_profile, 0:n_layer) ! Downwards flux (Wm-2) real(r_def), intent(out) :: flux_up(n_profile, 0:n_layer) ! Upwards flux (Wm-2) +real(r_def), intent(out) :: flux_direct_clear(n_profile, 0:n_layer) +! Direct (unscattered) downwards flux under clear-sky condition (Wm-2) +real(r_def), intent(out) :: flux_down_clear(n_profile, 0:n_layer) +! Downwards flux under clear-sky condition (Wm-2) +real(r_def), intent(out) :: flux_up_clear(n_profile, 0:n_layer) +! Upwards flux under clear-sky condition (Wm-2) real(r_def), intent(out) :: heating_rate(n_profile, n_layer) ! Heating rate (Ks-1) REAL(r_def), INTENT(inout), optional :: spectral_olr(:,:) ! Spectral OLR +real(r_def), intent(out), optional :: tot_cloud_cover(n_profile) +! Total cloud cover ! Dimensions: @@ -151,6 +168,7 @@ subroutine socrates_calc(Time_diag,control, spectrum, !DIAG Diagnostic logical :: used +real(r_def) :: zeros_cld(n_profile, n_layer), ten_microns_cld(n_profile, n_layer) call set_control(control) @@ -165,14 +183,23 @@ subroutine socrates_calc(Time_diag,control, spectrum, t_rad_surf, cos_zenith_angle, solar_irrad, orog_corr, & l_planet_grey_surface, planet_albedo, planet_emissivity) -call set_cld(control, dimen, spectrum, cld, n_profile) +! call set_cld(control, dimen, spectrum, cld, n_profile) + +zeros_cld = 0. +ten_microns_cld = 1. +call set_cld(cld, control, dimen, spectrum, n_profile, n_layer, & + liq_frac = cld_frac, & + ice_frac = zeros_cld, & + liq_mmr = mmr_cl_rad, & + ice_mmr = zeros_cld, & + liq_dim = reff_rad, & + ice_dim = zeros_cld ) call set_aer(control, dimen, spectrum, aer, n_profile) ! DEPENDS ON: radiance_calc call radiance_calc(control, dimen, spectrum, atm, cld, aer, bound, radout) - ! set heating rates and diagnostics do l=1, n_profile do i=1, n_layer @@ -187,10 +214,17 @@ subroutine socrates_calc(Time_diag,control, spectrum, flux_direct(l, i) = radout%flux_direct(l, i, 1) flux_down(l, i) = radout%flux_down(l, i, 1) flux_up(l, i) = radout%flux_up(l, i, 1) + + flux_direct_clear(l, i) = radout%flux_direct_clear(l, i, 1) + flux_down_clear(l, i) = radout%flux_down_clear(l, i, 1) + flux_up_clear(l, i) = radout%flux_up_clear(l, i, 1) end do if (present(spectral_olr)) then spectral_olr(l,:) = radout%flux_up_clear_band(l,0,:) endif + if (present(tot_cloud_cover)) then + tot_cloud_cover(l) = radout%tot_cloud_cover(l) + endif end do call deallocate_out(radout) @@ -201,6 +235,5 @@ subroutine socrates_calc(Time_diag,control, spectrum, call deallocate_bound(bound) call deallocate_atm(atm) - end subroutine socrates_calc end module socrates_calc_mod diff --git a/src/atmos_param/socrates/interface/socrates_config_mod.f90 b/src/atmos_param/socrates/interface/socrates_config_mod.f90 index 2689b940c..58ff3f64f 100644 --- a/src/atmos_param/socrates/interface/socrates_config_mod.f90 +++ b/src/atmos_param/socrates/interface/socrates_config_mod.f90 @@ -40,8 +40,11 @@ module socrates_config_mod real :: co2_ppmv = 300. !Default CO2 concentration in PPMV logical :: input_co2_mmr=.false. !Socrates wants input concentrations as mmr not vmr, so need to make sure input data supplied is converted if necessary + logical :: do_scm_ozone=.false. ! read single column ozone from namelist? note: ONLY when using SCM + real(r_def), dimension(100) :: scm_ozone = -1 ! input array for single column ozone. max number of levels = 100 + logical :: use_pressure_interp_for_half_levels = .False. !By default (.False.) does linear interpolation in height for half-level temperatures. True does linear interp using pressure. - + ! Incoming radiation options for namelist integer :: solday=0 ! if >0, do perpetual run corresponding to day of the year = solday \in [0,days per year] @@ -127,6 +130,6 @@ module socrates_config_mod inc_h2o, inc_co2, inc_co, inc_o3, inc_n2o, inc_ch4, inc_o2, & inc_so2, inc_cfc11, inc_cfc12, inc_cfc113, inc_hcfc22, inc_hfc134a, & use_pressure_interp_for_half_levels, & - frierson_solar_rad, del_sol, del_sw + frierson_solar_rad, del_sol, del_sw, do_scm_ozone, scm_ozone end module socrates_config_mod diff --git a/src/atmos_param/socrates/interface/socrates_interface.F90 b/src/atmos_param/socrates/interface/socrates_interface.F90 index 1a03b44f0..f970ba98c 100644 --- a/src/atmos_param/socrates/interface/socrates_interface.F90 +++ b/src/atmos_param/socrates/interface/socrates_interface.F90 @@ -34,8 +34,8 @@ MODULE socrates_interface_mod USE def_spectrum USE constants_mod, only: grav, rdgas, rvgas, cp_air USE fms_mod, only: stdlog, FATAL, WARNING, error_mesg - USE interpolator_mod, only: interpolate_type - USE soc_constants_mod + USE interpolator_mod, only: interpolate_type + USE soc_constants_mod IMPLICIT NONE @@ -51,18 +51,23 @@ MODULE socrates_interface_mod TYPE(StrCtrl) :: control_lw, control_lw_hires ! Diagnostic IDs, name, and missing value - INTEGER :: id_soc_spectral_olr + INTEGER :: id_soc_spectral_olr ! INTEGER :: id_soc_surf_spectrum_sw !not implemented yet INTEGER :: id_soc_tdt_sw, id_soc_tdt_lw, id_soc_tdt_rad - INTEGER :: id_soc_surf_flux_lw, id_soc_surf_flux_sw - INTEGER :: id_soc_surf_flux_lw_down, id_soc_surf_flux_sw_down + INTEGER :: id_soc_surf_flux_lw, id_soc_surf_flux_sw + INTEGER :: id_soc_surf_flux_lw_down, id_soc_surf_flux_sw_down INTEGER :: id_soc_flux_lw, id_soc_flux_sw + INTEGER :: id_soc_flux_lw_clr, id_soc_flux_sw_clr INTEGER :: id_soc_olr, id_soc_toa_sw - INTEGER :: id_soc_toa_sw_down + INTEGER :: id_soc_toa_sw_up, id_soc_toa_sw_down + INTEGER :: id_soc_olr_clr, id_soc_toa_sw_clr, id_soc_toa_sw_up_clr ! clear-sky case + INTEGER :: id_soc_surf_flux_sw_clr, id_soc_surf_flux_sw_down_clr, & + id_soc_surf_flux_lw_clr, id_soc_surf_flux_lw_down_clr INTEGER :: id_soc_ozone, id_soc_co2, id_soc_coszen INTEGER :: n_soc_bands_lw, n_soc_bands_sw INTEGER :: n_soc_bands_lw_hires, n_soc_bands_sw_hires INTEGER :: id_soc_bins_lw, id_soc_bins_sw + INTEGER :: id_soc_tot_cloud_cover CHARACTER(len=10), PARAMETER :: soc_mod_name = 'socrates' REAL :: missing_value = -999 @@ -70,21 +75,27 @@ MODULE socrates_interface_mod REAL :: dt_last !Time of last radiation calculation - used to tell whether it is time to recompute radiation or not REAL(r_def), allocatable, dimension(:,:,:) :: tdt_soc_sw_store, tdt_soc_lw_store - REAL(r_def), allocatable, dimension(:,:,:) :: thd_sw_flux_net_store, thd_lw_flux_net_store - REAL(r_def), allocatable, dimension(:,:,:) :: thd_co2_store, thd_ozone_store + REAL(r_def), allocatable, dimension(:,:,:) :: thd_sw_flux_net_store, thd_lw_flux_net_store, & + thd_sw_flux_clr_net_store, thd_lw_flux_clr_net_store + REAL(r_def), allocatable, dimension(:,:,:) :: thd_co2_store, thd_ozone_store REAL(r_def), allocatable, dimension(:,:) :: net_surf_sw_down_store, surf_lw_down_store, surf_lw_net_store, & surf_sw_down_store, toa_sw_down_store, & - toa_sw_store, olr_store, coszen_store - REAL(r_def), allocatable, dimension(:,:,:) :: outputted_soc_spectral_olr, spectral_olr_store + toa_sw_store, olr_store, coszen_store, & + toa_sw_clr_store, olr_clr_store, toa_sw_up_store, toa_sw_up_clr_store, & + net_surf_sw_down_clr_store, surf_lw_down_clr_store, & + surf_lw_net_clr_store, surf_sw_down_clr_store + + REAL(r_def), allocatable, dimension(:,:,:) :: outputted_soc_spectral_olr, spectral_olr_store, outputted_soc_spectral_olr_clr REAL(r_def), allocatable, dimension(:) :: soc_bins_lw, soc_bins_sw + LOGICAL :: do_clouds = .false. CONTAINS - SUBROUTINE socrates_init(is, ie, js, je, num_levels, axes, Time, lat, lonb, latb, delta_t_atmos) +SUBROUTINE socrates_init(is, ie, js, je, num_levels, axes, Time, lat, lonb, latb, delta_t_atmos, do_cloud_simple, do_cloud_spookie) !! Initialises Socrates spectra, arrays, and constants USE astronomy_mod, only: astronomy_init - USE interpolator_mod, only: interpolate_type, interpolator_init, ZERO + USE interpolator_mod, only: interpolate_type, interpolator_init, ZERO USE socrates_config_mod ! Arguments @@ -94,7 +105,8 @@ SUBROUTINE socrates_init(is, ie, js, je, num_levels, axes, Time, lat, lonb, latb INTEGER, INTENT(in) :: is, ie, js, je, num_levels REAL, INTENT(in) , DIMENSION(:,:) :: lat REAL, INTENT(in) , DIMENSION(:,:) :: lonb, latb - + LOGICAL, INTENT(IN) :: do_cloud_simple, do_cloud_spookie + integer :: io, stdlog_unit integer :: res, time_step_seconds real :: day_in_s_check @@ -111,77 +123,78 @@ SUBROUTINE socrates_init(is, ie, js, je, num_levels, axes, Time, lat, lonb, latb call close_file(nml_unit) endif #endif -stdlog_unit = stdlog() -write(stdlog_unit, socrates_rad_nml) + stdlog_unit = stdlog() + write(stdlog_unit, socrates_rad_nml) !Initialise astronomy call astronomy_init !Initialise variables related to radiation timestep - call get_time(delta_t_atmos,time_step_seconds) + call get_time(delta_t_atmos, time_step_seconds) - if (dt_rad .le. 0.) then - dt_rad = time_step_seconds !Make sure that dt_rad is set if it is not specified in the namelist - endif + if (dt_rad .le. 0.) then + dt_rad = time_step_seconds !Make sure that dt_rad is set if it is not specified in the namelist + endif - dt_last = -real(dt_rad) !make sure we are computing radiation at the first time step + dt_last = -real(dt_rad) !make sure we are computing radiation at the first time step - if (dt_rad .gt. time_step_seconds) then - res=mod(dt_rad, time_step_seconds) + if (dt_rad .gt. time_step_seconds) then + res=mod(dt_rad, time_step_seconds) - if (res.ne.0) then - call error_mesg( 'socrates_init', & - 'dt_rad must be an integer multiple of dt_atmos',FATAL) - endif + if (res.ne.0) then + call error_mesg( 'socrates_init', & + 'dt_rad must be an integer multiple of dt_atmos', FATAL) + endif - day_in_s_check=length_of_day() - res=mod(int(day_in_s_check), dt_rad) + day_in_s_check=length_of_day() + res=mod(int(day_in_s_check), dt_rad) - if (res.ne.0) then - call error_mesg( 'socrates_init', & - 'dt_rad does not fit into one day an integer number of times', WARNING) - endif + if (res.ne.0) then + call error_mesg( 'socrates_init', & + 'dt_rad does not fit into one day an integer number of times', WARNING) + endif + endif + if(dt_rad_avg .le. 0) dt_rad_avg = dt_rad - endif + if ((tidally_locked .eqv. .true.) .and. (frierson_solar_rad .eqv. .true.)) then + call error_mesg( 'socrates_init', & + 'tidally_locked and frierson_solar_rad cannot both be true', FATAL) + endif - if(dt_rad_avg .le. 0) dt_rad_avg = dt_rad - if ((tidally_locked.eq..true.) .and. (frierson_solar_rad .eq. .true.)) then + if (js == 1) then + if (lw_spectral_filename .eq. 'unset') then call error_mesg( 'socrates_init', & - 'tidally_locked and frierson_solar_rad cannot both be true',FATAL) + 'lw_spectral_filename is unset, and must point to a valid spectral file', FATAL) endif - IF (js == 1) THEN - - if (lw_spectral_filename .eq. 'unset') then - call error_mesg( 'socrates_init', & - 'lw_spectral_filename is unset, and must point to a valid spectral file',FATAL) - endif - - if (sw_spectral_filename .eq. 'unset') then - call error_mesg( 'socrates_init', & - 'sw_spectral_filename is unset, and must point to a valid spectral file',FATAL) - endif - ENDIF + if (sw_spectral_filename .eq. 'unset') then + call error_mesg( 'socrates_init', & + 'sw_spectral_filename is unset, and must point to a valid spectral file', FATAL) + endif + endif if (lw_hires_spectral_filename .eq. 'unset') then - IF (js == 1) THEN - call error_mesg( 'socrates_init', & - 'lw_hires_spectral_filename is unset, making equal to lw_spectral_filename',WARNING) - endif + if (js == 1) then + call error_mesg( 'socrates_init', & + 'lw_hires_spectral_filename is unset, making equal to lw_spectral_filename', WARNING) + endif lw_hires_spectral_filename = lw_spectral_filename endif if (sw_hires_spectral_filename .eq. 'unset') then - IF (js == 1) THEN - call error_mesg( 'socrates_init', & - 'sw_hires_spectral_filename is unset, making equal to sw_spectral_filename',WARNING) - endif + if (js == 1) then + call error_mesg( 'socrates_init', & + 'sw_hires_spectral_filename is unset, making equal to sw_spectral_filename', WARNING) + endif sw_hires_spectral_filename = sw_spectral_filename endif - + + if ((do_cloud_simple) .or. (do_cloud_spookie)) then + do_clouds = .true. + endif ! Socrates spectral files -- should be set by namelist control_lw%spectral_file = lw_spectral_filename @@ -195,12 +208,12 @@ SUBROUTINE socrates_init(is, ie, js, je, num_levels, axes, Time, lat, lonb, latb CALL read_spectrum(control_lw_hires%spectral_file,spectrum_lw_hires) CALL read_spectrum(control_sw%spectral_file,spectrum_sw) CALL read_spectrum(control_sw_hires%spectral_file,spectrum_sw_hires) - + ! Set Socrates configuration - CALL read_control(control_lw,spectrum_lw) - CALL read_control(control_lw_hires,spectrum_lw_hires) - CALL read_control(control_sw,spectrum_sw) - CALL read_control(control_sw_hires,spectrum_sw_hires) + CALL read_control(control_lw,spectrum_lw, do_clouds) + CALL read_control(control_lw_hires,spectrum_lw_hires, do_clouds) + CALL read_control(control_sw,spectrum_sw, do_clouds) + CALL read_control(control_sw_hires,spectrum_sw_hires, do_clouds) ! Specify LW and SW setups control_sw%isolir=1 @@ -210,18 +223,18 @@ SUBROUTINE socrates_init(is, ie, js, je, num_levels, axes, Time, lat, lonb, latb if(socrates_hires_mode) then allocate(soc_bins_lw(spectrum_lw_hires%dim%nd_band)) - allocate(soc_bins_sw(spectrum_sw_hires%dim%nd_band)) - soc_bins_lw = spectrum_lw_hires%Basic%wavelength_long - soc_bins_sw = spectrum_sw_hires%Basic%wavelength_short + allocate(soc_bins_sw(spectrum_sw_hires%dim%nd_band)) + soc_bins_lw = spectrum_lw_hires%Basic%wavelength_long + soc_bins_sw = spectrum_sw_hires%Basic%wavelength_short else allocate(soc_bins_lw(spectrum_lw%dim%nd_band)) - allocate(soc_bins_sw(spectrum_sw%dim%nd_band)) - soc_bins_lw = spectrum_lw%Basic%wavelength_long - soc_bins_sw = spectrum_sw%Basic%wavelength_short - endif - - !Need to actually give bins arrays values - + allocate(soc_bins_sw(spectrum_sw%dim%nd_band)) + soc_bins_lw = spectrum_lw%Basic%wavelength_long + soc_bins_sw = spectrum_sw%Basic%wavelength_short + endif + + !Need to actually give bins arrays values + id_soc_bins_lw = diag_axis_init('soc_bins_lw', soc_bins_lw, 'cm^-1', 'n', 'socrates lw spectral bin centers', set_name='socrates_lw_bins') id_soc_bins_sw = diag_axis_init('soc_bins_sw', soc_bins_sw, 'cm^-1', 'n', 'socrates sw spectral bin centers', set_name='socrates_sw_bins') @@ -230,103 +243,161 @@ SUBROUTINE socrates_init(is, ie, js, je, num_levels, axes, Time, lat, lonb, latb id_soc_spectral_olr = & register_diag_field ( soc_mod_name, 'soc_spectral_olr',(/ axes(1:2), id_soc_bins_lw/) , Time, & 'socrates substellar LW OLR spectrum', & - 'watts/m2', missing_value=missing_value ) + 'watts/m2', missing_value=missing_value ) !Not implemented yet ! id_soc_surf_spectrum_sw = & ! register_diag_field ( soc_mod_name, 'soc_surf_spectrum_sw',(/ axes(1:2), id_soc_bins_sw/) , Time, & ! 'socrates substellar SW surface spectrum', & -! 'watts/m2', missing_value=missing_value ) +! 'watts/m2', missing_value=missing_value ) id_soc_tdt_lw = & register_diag_field ( soc_mod_name, 'soc_tdt_lw', axes(1:3), Time, & 'socrates Temperature tendency due to LW radiation', & - 'K/s', missing_value=missing_value ) + 'K/s', missing_value=missing_value ) id_soc_tdt_sw = & register_diag_field ( soc_mod_name, 'soc_tdt_sw', axes(1:3), Time, & 'socrates Temperature tendency due to SW radiation', & - 'K/s', missing_value=missing_value ) + 'K/s', missing_value=missing_value ) id_soc_tdt_rad = & register_diag_field ( soc_mod_name, 'soc_tdt_rad', axes(1:3), Time, & 'socrates Temperature tendency due to radiation', & - 'K/s', missing_value=missing_value ) + 'K/s', missing_value=missing_value ) id_soc_surf_flux_lw = & register_diag_field ( soc_mod_name, 'soc_surf_flux_lw', axes(1:2), Time, & 'socrates Net LW surface flux (up)', & - 'watts/m2', missing_value=missing_value ) + 'watts/m2', missing_value=missing_value ) id_soc_surf_flux_lw_down = & register_diag_field ( soc_mod_name, 'soc_surf_flux_lw_down', axes(1:2), Time, & 'socrates LW surface flux down', & - 'watts/m2', missing_value=missing_value ) + 'watts/m2', missing_value=missing_value ) id_soc_surf_flux_sw = & register_diag_field ( soc_mod_name, 'soc_surf_flux_sw', axes(1:2), Time, & - 'socrates Net SW surface flux (down)', & - 'watts/m2', missing_value=missing_value ) + 'socrates Net SW surface flux (down)', & + 'watts/m2', missing_value=missing_value ) id_soc_surf_flux_sw_down = & register_diag_field ( soc_mod_name, 'soc_surf_flux_sw_down', axes(1:2), Time, & - 'socrates SW surface flux down', & - 'watts/m2', missing_value=missing_value ) + 'socrates SW surface flux down', & + 'watts/m2', missing_value=missing_value ) id_soc_olr = & register_diag_field ( soc_mod_name, 'soc_olr', axes(1:2), Time, & 'socrates TOA LW flux (up)', & - 'watts/m2', missing_value=missing_value ) + 'watts/m2', missing_value=missing_value ) id_soc_toa_sw = & register_diag_field ( soc_mod_name, 'soc_toa_sw', axes(1:2), Time, & - 'socrates Net TOA SW flux (down)', & - 'watts/m2', missing_value=missing_value ) + 'socrates Net TOA SW flux (down)', & + 'watts/m2', missing_value=missing_value ) + + id_soc_toa_sw_up = & + register_diag_field ( soc_mod_name, 'soc_toa_sw_up', axes(1:2), Time, & + 'socrates upward TOA SW flux', & + 'watts/m2', missing_value=missing_value ) id_soc_toa_sw_down = & register_diag_field ( soc_mod_name, 'soc_toa_sw_down', axes(1:2), Time, & - 'socrates TOA SW flux down', & - 'watts/m2', missing_value=missing_value ) + 'socrates TOA SW flux down', & + 'watts/m2', missing_value=missing_value ) + + id_soc_olr_clr = & + register_diag_field ( soc_mod_name, 'soc_olr_clr', axes(1:2), Time, & + 'clear-sky socrates TOA LW flux (up)', & + 'watts/m2', missing_value=missing_value ) + + id_soc_toa_sw_clr = & + register_diag_field ( soc_mod_name, 'soc_toa_sw_clr', axes(1:2), Time, & + 'clear-sky socrates Net TOA SW flux (down)', & + 'watts/m2', missing_value=missing_value ) + + id_soc_toa_sw_up_clr = & + register_diag_field ( soc_mod_name, 'soc_toa_sw_up_clr', axes(1:2), Time, & + 'clear-sky socrates upward TOA SW flux', & + 'watts/m2', missing_value=missing_value ) + + id_soc_flux_lw_clr = & + register_diag_field ( soc_mod_name, 'soc_flux_lw_clr', (/axes(1),axes(2),axes(4)/), Time, & + 'clear-sky socrates Net LW flux (up)', & + 'watts/m2', missing_value=missing_value ) + + id_soc_flux_sw_clr = & + register_diag_field ( soc_mod_name, 'soc_flux_sw_clr', (/axes(1),axes(2),axes(4)/), Time, & + 'clear-sky socrates Net SW flux (up)', & + 'watts/m2', missing_value=missing_value ) + + id_soc_surf_flux_lw_clr = & + register_diag_field ( soc_mod_name, 'soc_surf_flux_lw_clr', axes(1:2), Time, & + 'clear-sky socrates Net LW surface flux (up)', & + 'watts/m2', missing_value=missing_value ) + + id_soc_surf_flux_lw_down_clr = & + register_diag_field ( soc_mod_name, 'soc_surf_flux_lw_down_clr', axes(1:2), Time, & + 'clear-sky socrates LW surface flux down', & + 'watts/m2', missing_value=missing_value ) + + id_soc_surf_flux_sw_clr = & + register_diag_field ( soc_mod_name, 'soc_surf_flux_sw_clr', axes(1:2), Time, & + 'clear-sky socrates Net SW surface flux (down)', & + 'watts/m2', missing_value=missing_value ) + + id_soc_surf_flux_sw_down_clr = & + register_diag_field ( soc_mod_name, 'soc_surf_flux_sw_down_clr', axes(1:2), Time, & + 'clear-sky socrates SW surface flux down', & + 'watts/m2', missing_value=missing_value ) id_soc_flux_lw = & register_diag_field ( soc_mod_name, 'soc_flux_lw', (/axes(1),axes(2),axes(4)/), Time, & 'socrates Net LW flux (up)', & - 'watts/m2', missing_value=missing_value ) + 'watts/m2', missing_value=missing_value ) id_soc_flux_sw = & register_diag_field ( soc_mod_name, 'soc_flux_sw', (/axes(1),axes(2),axes(4)/), Time, & - 'socrates Net SW flux (up)', & - 'watts/m2', missing_value=missing_value ) + 'socrates Net SW flux (up)', & + 'watts/m2', missing_value=missing_value ) id_soc_coszen = & register_diag_field ( soc_mod_name, 'soc_coszen', axes(1:2), Time, & 'socrates Cosine(zenith_angle)', & - 'none', missing_value=missing_value ) + 'none', missing_value=missing_value ) id_soc_ozone = & register_diag_field ( soc_mod_name, 'soc_ozone', axes(1:3), Time, & 'socrates Ozone', & - 'mmr', missing_value=missing_value ) + 'mmr', missing_value=missing_value ) id_soc_co2 = & register_diag_field ( soc_mod_name, 'soc_co2', axes(1:3), Time, & 'socrates Co2', & - 'mmr', missing_value=missing_value ) + 'mmr', missing_value=missing_value ) + + id_soc_tot_cloud_cover = & + register_diag_field ( soc_mod_name, 'soc_tot_cloud_cover', axes(1:2), Time, & + 'socrates Total cloud cover', & + '%', missing_value=missing_value ) + if(do_read_ozone)then + call interpolator_init (o3_interp, trim(ozone_file_name)//'.nc', lonb, latb, data_out_of_bounds=(/ZERO/)) + endif + + if(do_read_co2)then + call interpolator_init (co2_interp, trim(co2_file_name)//'.nc', lonb, latb, data_out_of_bounds=(/ZERO/)) + endif - if(do_read_ozone)then - call interpolator_init (o3_interp, trim(ozone_file_name)//'.nc', lonb, latb, data_out_of_bounds=(/ZERO/)) + if(do_scm_ozone)then + call error_mesg('socrates_interface', & + 'Input o3 will be read in exactly as specified in input (i.e. no plevel interpolation will be performed). Ensure it is specified correctly in namelist. ONLY FOR USE WITH SINGLE COLUMN MODEL.', & + WARNING) endif - - if(do_read_co2)then - call interpolator_init (co2_interp, trim(co2_file_name)//'.nc', lonb, latb, data_out_of_bounds=(/ZERO/)) - endif if (mod((size(lonb,1)-1)*(size(latb,1)-1), chunk_size) .ne. 0) then - call error_mesg( 'socrates_init', & - 'chunk_size must equally divide number of points per processor, which it currently does not.',FATAL) - + 'chunk_size must equally divide number of points per processor, which it currently does not.', FATAL) endif ! Number of bands @@ -335,7 +406,8 @@ SUBROUTINE socrates_init(is, ie, js, je, num_levels, axes, Time, lat, lonb, latb n_soc_bands_sw = spectrum_sw%dim%nd_band n_soc_bands_sw_hires = spectrum_sw_hires%dim%nd_band - if (socrates_hires_mode == .True.) then + + if (socrates_hires_mode .eqv. .True.) then allocate(outputted_soc_spectral_olr(size(lonb,1)-1, size(latb,2)-1, n_soc_bands_lw_hires)) else allocate(outputted_soc_spectral_olr(size(lonb,1)-1, size(latb,2)-1, n_soc_bands_lw )) @@ -343,59 +415,101 @@ SUBROUTINE socrates_init(is, ie, js, je, num_levels, axes, Time, lat, lonb, latb if(store_intermediate_rad) then - ! required for computation + ! required for computation allocate(tdt_soc_sw_store(size(lonb,1)-1, size(latb,2)-1, num_levels)) allocate(tdt_soc_lw_store(size(lonb,1)-1, size(latb,2)-1, num_levels)) allocate(net_surf_sw_down_store(size(lonb,1)-1, size(latb,2)-1)) allocate(surf_lw_down_store(size(lonb,1)-1, size(latb,2)-1)) - ! only required for output + ! only required for output if (id_soc_surf_flux_lw > 0) then allocate(surf_lw_net_store(size(lonb,1)-1, size(latb,2)-1)) endif - if (id_soc_flux_lw > 0) then + if (id_soc_flux_lw > 0) then allocate(thd_lw_flux_net_store(size(lonb,1)-1, size(latb,2)-1, num_levels+1)) - endif + endif - if (id_soc_flux_sw > 0) then + if (id_soc_flux_sw > 0) then allocate(thd_sw_flux_net_store(size(lonb,1)-1, size(latb,2)-1, num_levels+1)) - endif + endif - if (id_soc_surf_flux_sw_down > 0) then + if (id_soc_surf_flux_sw_down > 0) then allocate(surf_sw_down_store(size(lonb,1)-1, size(latb,2)-1)) - endif + endif - if (id_soc_olr > 0) then + if (id_soc_olr > 0) then allocate(olr_store(size(lonb,1)-1, size(latb,2)-1)) - endif + endif - if (id_soc_toa_sw > 0) then + if (id_soc_toa_sw > 0) then allocate(toa_sw_store(size(lonb,1)-1, size(latb,2)-1)) endif - if (id_soc_toa_sw_down > 0) then + + if (id_soc_toa_sw_down > 0) then allocate(toa_sw_down_store(size(lonb,1)-1, size(latb,2)-1)) endif - if (id_soc_coszen > 0) then + + if (id_soc_toa_sw_up > 0) then + allocate(toa_sw_up_store(size(lonb,1)-1, size(latb,2)-1)) + endif + + if (id_soc_flux_lw_clr > 0) then + allocate(thd_lw_flux_clr_net_store(size(lonb,1)-1, size(latb,2)-1, num_levels+1)) + endif + + if (id_soc_flux_sw_clr > 0) then + allocate(thd_sw_flux_clr_net_store(size(lonb,1)-1, size(latb,2)-1, num_levels+1)) + endif + + if (id_soc_olr_clr > 0) then + allocate(olr_clr_store(size(lonb,1)-1, size(latb,2)-1)) + endif + + if (id_soc_toa_sw_clr > 0) then + allocate(toa_sw_clr_store(size(lonb,1)-1, size(latb,2)-1)) + endif + + if (id_soc_toa_sw_up_clr > 0) then + allocate(toa_sw_up_clr_store(size(lonb,1)-1, size(latb,2)-1)) + endif + + if (id_soc_surf_flux_sw_clr > 0) then + allocate(net_surf_sw_down_clr_store(size(lonb,1)-1, size(latb,2)-1)) + endif + + if (id_soc_surf_flux_sw_down_clr > 0) then + allocate(surf_sw_down_clr_store(size(lonb,1)-1, size(latb,2)-1)) + endif + + if (id_soc_surf_flux_lw_clr > 0) then + allocate(surf_lw_net_clr_store(size(lonb,1)-1, size(latb,2)-1)) + endif + + if (id_soc_surf_flux_lw_down_clr > 0) then + allocate(surf_lw_down_clr_store(size(lonb,1)-1, size(latb,2)-1)) + endif + + if (id_soc_coszen > 0) then allocate(coszen_store(size(lonb,1)-1, size(latb,2)-1)) endif - if (id_soc_ozone > 0) then + if (id_soc_ozone > 0) then allocate(thd_ozone_store(size(lonb,1)-1, size(latb,2)-1, num_levels)) - endif + endif - if (id_soc_co2 > 0 ) then + if (id_soc_co2 > 0 ) then allocate(thd_co2_store(size(lonb,1)-1, size(latb,2)-1, num_levels)) endif - ! spectral output currently not available as required axis not present in diag file - if (id_soc_spectral_olr > 0) then - if (socrates_hires_mode == .True.) then + ! spectral output currently not available as required axis not present in diag file + if (id_soc_spectral_olr > 0) then + if (socrates_hires_mode .eqv. .True.) then allocate(spectral_olr_store(size(lonb,1)-1, size(latb,2)-1, n_soc_bands_lw_hires)) else allocate(spectral_olr_store(size(lonb,1)-1, size(latb,2)-1, n_soc_bands_lw )) endif - endif + endif endif @@ -421,7 +535,7 @@ SUBROUTINE socrates_init(is, ie, js, je, num_levels, axes, Time, lat, lonb, latb PRINT*, ' ' PRINT*, '-----------------------------------' PRINT*, ' ' - end if + END IF return end subroutine socrates_init @@ -431,8 +545,16 @@ end subroutine socrates_init ! Set up the call to the Socrates radiation scheme ! ----------------------------------------------------------------------------- subroutine socrates_interface(Time_diag, rlat, rlon, soc_lw_mode, & - fms_temp, fms_spec_hum, fms_ozone, fms_co2, fms_t_surf, fms_p_full, fms_p_half, fms_z_full, fms_z_half, fms_albedo, fms_coszen, fms_rrsun, n_profile, n_layer, & - output_heating_rate, output_flux_down, output_flux_up, output_soc_spectral_olr, output_flux_direct, t_half_level_out ) + fms_temp, fms_spec_hum, fms_ozone, fms_co2, fms_t_surf, & + fms_p_full, fms_p_half, fms_z_full, fms_z_half, fms_albedo, & + fms_coszen, fms_rrsun, n_profile, n_layer, & + fms_cld_frac, fms_reff_rad, fms_mmr_cl_rad, & + output_heating_rate, output_flux_down, output_flux_up, & + output_flux_down_clr, output_flux_up_clr, & + do_cloud_simple, do_cloud_spookie, & + !optionals + output_soc_spectral_olr, output_flux_direct, & + output_flux_direct_clr, t_half_level_out, tot_cloud_cover ) use realtype_rd use read_control_mod @@ -469,16 +591,24 @@ subroutine socrates_interface(Time_diag, rlat, rlon, soc_lw_mode, & real(r_def), intent(in) :: rlon(:,:) real(r_def), intent(in) :: rlat(:,:) real(r_def), intent(in) :: fms_z_full(:,:,:), fms_z_half(:,:,:) - real(r_def), intent(in) :: fms_rrsun + real(r_def), intent(in) :: fms_rrsun + real(r_def), intent(in) :: fms_cld_frac(:,:,:), fms_reff_rad(:,:,:), fms_mmr_cl_rad(:,:,:) + logical, intent(in) :: do_cloud_simple, do_cloud_spookie ! Output arrays real(r_def), intent(out) :: output_heating_rate(:,:,:) real(r_def), intent(out) :: output_flux_up(:,:,:) real(r_def), intent(out) :: output_flux_down(:,:,:) - real(r_def), intent(out), optional :: output_flux_direct(:,:,:) + + real(r_def), intent(out) :: output_flux_up_clr(:,:,:) + real(r_def), intent(out) :: output_flux_down_clr(:,:,:) + + real(r_def), intent(out), optional :: output_flux_direct(:,:,:) + real(r_def), intent(out), optional :: output_flux_direct_clr(:,:,:) real(r_def), intent(out), optional :: output_soc_spectral_olr(:,:,:) real(r_def), intent(out), optional :: t_half_level_out(size(fms_temp,1),size(fms_temp,2),size(fms_temp,3)+1) + real(r_def), intent(out), optional :: tot_cloud_cover(:,:) ! Hi-res output INTEGER, PARAMETER :: out_unit=20 @@ -486,14 +616,16 @@ subroutine socrates_interface(Time_diag, rlat, rlon, soc_lw_mode, & REAL :: soc_spectral_olr(n_profile, size(outputted_soc_spectral_olr,3)) ! Arrays to send to Socrates - real, dimension(n_profile, n_layer) :: input_p, input_t, input_mixing_ratio, & + real(r_def), dimension(n_profile, n_layer) :: input_p, input_t, input_mixing_ratio, & input_d_mass, input_density, input_layer_heat_capacity, & soc_heating_rate, input_o3_mixing_ratio, & - input_co2_mixing_ratio,z_full_reshaped - real, dimension(n_profile, 0:n_layer) :: input_p_level, input_t_level, soc_flux_direct, & - soc_flux_down, soc_flux_up, z_half_reshaped - real, dimension(n_profile) :: input_t_surf, input_cos_zenith_angle, input_solar_irrad, & - input_orog_corr, input_planet_albedo + input_co2_mixing_ratio,z_full_reshaped, input_cld_frac, input_reff_rad, input_mmr_cl_rad + + real(r_def), dimension(n_profile, 0:n_layer) :: input_p_level, input_t_level, soc_flux_direct, & + soc_flux_down, soc_flux_up, soc_flux_direct_clr, soc_flux_down_clr, soc_flux_up_clr, z_half_reshaped + + real(r_def), dimension(n_profile) :: input_t_surf, input_cos_zenith_angle, input_solar_irrad, & + input_orog_corr, input_planet_albedo, soc_tot_cloud_cover ! Socrates options @@ -527,188 +659,222 @@ subroutine socrates_interface(Time_diag, rlat, rlon, soc_lw_mode, & sj = size(fms_temp,2) sk = size(fms_temp,3) - !Set input T, p, p_level, and mixing ratio profiles - input_t = reshape(fms_temp(:,:,:),(/si*sj,sk /)) - input_p = reshape(fms_p_full(:,:,:),(/si*sj,sk /)) - input_p_level = reshape(fms_p_half(:,:,:),(/si*sj,sk+1 /)) - - if (account_for_effect_of_water == .true.) then - input_mixing_ratio = reshape(fms_spec_hum(:,:,:) / (1. - fms_spec_hum(:,:,:)),(/si*sj,sk /)) !Mass mixing ratio = q / (1-q) - else - input_mixing_ratio = 0.0 - endif - - if (account_for_effect_of_ozone == .true.) then - input_o3_mixing_ratio = reshape(fms_ozone(:,:,:),(/si*sj,sk /)) - else - input_o3_mixing_ratio = 0.0 - endif - - input_co2_mixing_ratio = reshape(fms_co2(:,:,:),(/si*sj,sk /)) - - !------------- - - !Default parameters - input_cos_zenith_angle = reshape((fms_coszen(:,:)),(/si*sj /)) - input_orog_corr = 0.0 - input_planet_albedo = reshape(fms_albedo(:,:),(/n_profile /)) - - !Set tide-locked flux - should be set by namelist eventually! - input_solar_irrad = stellar_constant * fms_rrsun ! * fms_rrsun includes effect of eccentricity if using diurnal_solar, rrsun = 1 if tidally locked - input_t_surf = reshape(fms_t_surf(:,:),(/si*sj /)) - z_full_reshaped = reshape(fms_z_full(:,:,:), (/si*sj, sk/)) - z_half_reshaped = reshape(fms_z_half(:,:,:), (/si*sj, sk+1/)) - - !-------------- - !Set input t_level by scaling t - NEEDS TO CHANGE! - if (use_pressure_interp_for_half_levels) then - DO i = nlat, nlat - DO k = 0,n_layer - input_t_level(:,k) = input_t(:,k) + (input_t(:,k+1)-input_t(:,k)) * ((input_p_level(:,k)-input_p(:,k))/(input_p(:,k+1)-input_p(:,k))) - END DO + + !Set input T, p, p_level, and mixing ratio profiles + input_t = reshape(fms_temp(:,:,:),(/si*sj,sk /)) + input_p = reshape(fms_p_full(:,:,:),(/si*sj,sk /)) + input_p_level = reshape(fms_p_half(:,:,:),(/si*sj,sk+1 /)) + + input_cld_frac = reshape(fms_cld_frac(:,:,:),(/si*sj,sk /)) + input_reff_rad = reshape(fms_reff_rad(:,:,:),(/si*sj,sk /)) + input_mmr_cl_rad = reshape(fms_mmr_cl_rad(:,:,:),(/si*sj,sk/)) + + if (account_for_effect_of_water .eqv. .true.) then + input_mixing_ratio = reshape(fms_spec_hum(:,:,:) / (1. - fms_spec_hum(:,:,:)),(/si*sj,sk /)) !Mass mixing ratio = q / (1-q) + else + input_mixing_ratio = 0.0 + endif + + if (account_for_effect_of_ozone .eqv. .true.) then + input_o3_mixing_ratio = reshape(fms_ozone(:,:,:),(/si*sj,sk /)) + else + input_o3_mixing_ratio = 0.0 + endif + + input_co2_mixing_ratio = reshape(fms_co2(:,:,:),(/si*sj,sk /)) + + !------------- + + !Default parameters + input_cos_zenith_angle = reshape((fms_coszen(:,:)),(/si*sj /)) + input_orog_corr = 0.0 + input_planet_albedo = reshape(fms_albedo(:,:),(/n_profile /)) + + !Set tide-locked flux - should be set by namelist eventually! + input_solar_irrad = stellar_constant * fms_rrsun ! * fms_rrsun includes effect of eccentricity if using diurnal_solar, rrsun = 1 if tidally locked + input_t_surf = reshape(fms_t_surf(:,:),(/si*sj /)) + z_full_reshaped = reshape(fms_z_full(:,:,:), (/si*sj, sk/)) + z_half_reshaped = reshape(fms_z_half(:,:,:), (/si*sj, sk+1/)) + + !-------------- + !Set input t_level by scaling t - NEEDS TO CHANGE! + if (use_pressure_interp_for_half_levels) then + DO i = nlat, nlat + DO k = 0,n_layer + input_t_level(:,k) = input_t(:,k) + (input_t(:,k+1)-input_t(:,k)) * ((input_p_level(:,k)-input_p(:,k))/(input_p(:,k+1)-input_p(:,k))) + END DO ! input_t_level(:,n_layer) = input_t(:,n_layer) + input_t(:,n_layer) - input_t_level(:,n_layer-1) - input_t_level(:,n_layer) = input_t(:,n_layer) + (input_t(:,n_layer)-input_t(:,n_layer-1)) * ((input_p_level(:,n_layer)-input_p(:,n_layer))/(input_p(:,n_layer)-input_p(:,n_layer-1))) - + input_t_level(:,n_layer) = input_t(:,n_layer) + (input_t(:,n_layer)-input_t(:,n_layer-1)) * ((input_p_level(:,n_layer)-input_p(:,n_layer))/(input_p(:,n_layer)-input_p(:,n_layer-1))) + ! input_t_level(:,0) = input_t(:,1) - (input_t_level(:,1) - input_t(:,1)) - input_t_level(:,0) = input_t(:,1) + (input_t(:,2)-input_t(:,1)) * ((input_p_level(:,0)-input_p(:,1))/(input_p(:,2)-input_p(:,1))) - - END DO - else - - call interp_temp(z_full_reshaped,z_half_reshaped,input_t, input_t_level) - - endif - - if (present(t_half_level_out)) then - t_half_level_out(:,:,:) = reshape(input_t_level,(/si,sj,sk+1 /)) - endif - - !Set input dry mass, density, and heat capacity profiles - DO i=n_layer, 1, -1 - input_d_mass(:,i) = (input_p_level(:,i)-input_p_level(:,i-1))/grav - input_density(:,i) = input_p(:,i)/(rdgas*input_t(:,i)) - input_layer_heat_capacity(:,i) = input_d_mass(:,i)*cp_air - END DO - - - ! Zero heating rate - soc_heating_rate = 0.0 - - ! Test if LW or SW mode - if (soc_lw_mode == .TRUE.) then - control_lw%isolir = 2 - CALL read_control(control_lw, spectrum_lw) - if (socrates_hires_mode == .FALSE.) then - control_calc = control_lw - spectrum_calc = spectrum_lw - else - control_calc = control_lw_hires - spectrum_calc = spectrum_lw_hires - end if - - else - control_sw%isolir = 1 - CALL read_control(control_sw, spectrum_sw) - if(socrates_hires_mode == .FALSE.) then - control_calc = control_sw - spectrum_calc = spectrum_sw - else - control_calc = control_sw_hires - spectrum_calc = spectrum_sw_hires - end if - - end if - - - ! Do calculation - CALL read_control(control_calc, spectrum_calc) + + input_t_level(:,0) = input_t(:,1) + (input_t(:,2)-input_t(:,1)) * ((input_p_level(:,0)-input_p(:,1))/(input_p(:,2)-input_p(:,1))) + + END DO + else + + call interp_temp(z_full_reshaped,z_half_reshaped,input_t, input_t_level) + + endif + + if (present(t_half_level_out)) then + t_half_level_out(:,:,:) = reshape(input_t_level,(/si,sj,sk+1 /)) + endif + + !Set input dry mass, density, and heat capacity profiles + DO i=n_layer, 1, -1 + input_d_mass(:,i) = (input_p_level(:,i)-input_p_level(:,i-1))/grav + input_density(:,i) = input_p(:,i)/(rdgas*input_t(:,i)) + input_layer_heat_capacity(:,i) = input_d_mass(:,i)*cp_air + END DO + + ! Zero heating rate + soc_heating_rate = 0.0 + + if (do_cloud_simple .or. do_cloud_spookie) then + do_clouds = .true. + endif + + ! Test if LW or SW mode + if (soc_lw_mode .eqv. .TRUE.) then + control_lw%isolir = 2 + CALL read_control(control_lw, spectrum_lw, do_clouds) + if (socrates_hires_mode .eqv. .FALSE.) then + control_calc = control_lw + spectrum_calc = spectrum_lw + else + control_calc = control_lw_hires + spectrum_calc = spectrum_lw_hires + end if + + else + control_sw%isolir = 1 + CALL read_control(control_sw, spectrum_sw, do_clouds) + if(socrates_hires_mode .eqv. .FALSE.) then + control_calc = control_sw + spectrum_calc = spectrum_sw + else + control_calc = control_sw_hires + spectrum_calc = spectrum_sw_hires + end if + + end if + + ! Do calculation + CALL read_control(control_calc, spectrum_calc, do_clouds) + n_chunk_loop = (si*sj)/chunk_size n_profile_chunk = n_profile / n_chunk_loop - DO i_chunk=1,n_chunk_loop - - idx_chunk_start = (i_chunk-1)*chunk_size + 1 - idx_chunk_end = (i_chunk)*chunk_size - - if (soc_lw_mode==.TRUE.) then - - CALL socrates_calc(Time_diag, control_calc, spectrum_calc, & - n_profile_chunk, n_layer, input_n_cloud_layer, input_n_aer_mode, & - input_cld_subcol_gen, input_cld_subcol_req, & - input_p(idx_chunk_start:idx_chunk_end,:), & - input_t(idx_chunk_start:idx_chunk_end,:), & - input_t_level(idx_chunk_start:idx_chunk_end,:), & - input_d_mass(idx_chunk_start:idx_chunk_end,:), & - input_density(idx_chunk_start:idx_chunk_end,:), & - input_mixing_ratio(idx_chunk_start:idx_chunk_end,:), & - input_o3_mixing_ratio(idx_chunk_start:idx_chunk_end,:), & - input_co2_mixing_ratio(idx_chunk_start:idx_chunk_end,:), & - input_t_surf(idx_chunk_start:idx_chunk_end), & - input_cos_zenith_angle(idx_chunk_start:idx_chunk_end), & - input_solar_irrad(idx_chunk_start:idx_chunk_end), & - input_orog_corr(idx_chunk_start:idx_chunk_end), & - l_planet_grey_surface, & - input_planet_albedo(idx_chunk_start:idx_chunk_end), & - input_planet_emissivity, & - input_layer_heat_capacity(idx_chunk_start:idx_chunk_end,:), & - soc_flux_direct(idx_chunk_start:idx_chunk_end,:), & - soc_flux_down(idx_chunk_start:idx_chunk_end,:), & - soc_flux_up(idx_chunk_start:idx_chunk_end,:), & - soc_heating_rate(idx_chunk_start:idx_chunk_end,:), & - soc_spectral_olr(idx_chunk_start:idx_chunk_end,:)) - - else - CALL socrates_calc(Time_diag, control_calc, spectrum_calc, & - n_profile_chunk, n_layer, input_n_cloud_layer, input_n_aer_mode, & - input_cld_subcol_gen, input_cld_subcol_req, & - input_p(idx_chunk_start:idx_chunk_end,:), & - input_t(idx_chunk_start:idx_chunk_end,:), & - input_t_level(idx_chunk_start:idx_chunk_end,:), & - input_d_mass(idx_chunk_start:idx_chunk_end,:), & - input_density(idx_chunk_start:idx_chunk_end,:), & - input_mixing_ratio(idx_chunk_start:idx_chunk_end,:), & - input_o3_mixing_ratio(idx_chunk_start:idx_chunk_end,:), & - input_co2_mixing_ratio(idx_chunk_start:idx_chunk_end,:), & - input_t_surf(idx_chunk_start:idx_chunk_end), & - input_cos_zenith_angle(idx_chunk_start:idx_chunk_end), & - input_solar_irrad(idx_chunk_start:idx_chunk_end), & - input_orog_corr(idx_chunk_start:idx_chunk_end), & - l_planet_grey_surface, & - input_planet_albedo(idx_chunk_start:idx_chunk_end), & - input_planet_emissivity, & - input_layer_heat_capacity(idx_chunk_start:idx_chunk_end,:), & - soc_flux_direct(idx_chunk_start:idx_chunk_end,:), & - soc_flux_down(idx_chunk_start:idx_chunk_end,:), & - soc_flux_up(idx_chunk_start:idx_chunk_end,:), & - soc_heating_rate(idx_chunk_start:idx_chunk_end,:)) - endif - - ENDDO - - ! Set output arrays - output_flux_up(:,:,:) = reshape(soc_flux_up(:,:),(/si,sj,sk+1 /)) - output_flux_down(:,:,:) = reshape(soc_flux_down(:,:),(/si,sj,sk+1 /)) - - if(present(output_flux_direct)) then - output_flux_direct(:,:,:) = reshape(soc_flux_direct(:,:),(/si,sj,sk+1 /)) - endif - - output_heating_rate(:,:,:) = reshape(soc_heating_rate(:,:),(/si,sj,sk /)) - - if (soc_lw_mode == .TRUE.) then - output_soc_spectral_olr(:,:,:) = reshape(soc_spectral_olr(:,:),(/si,sj,int(n_soc_bands_lw,i_def) /)) - endif + + DO i_chunk=1,n_chunk_loop + + idx_chunk_start = (i_chunk-1)*chunk_size + 1 + idx_chunk_end = (i_chunk)*chunk_size + + if (soc_lw_mode .eqv. .TRUE.) then + CALL socrates_calc(Time_diag, control_calc, spectrum_calc, & + n_profile_chunk, n_layer, input_n_cloud_layer, input_n_aer_mode, & + input_cld_subcol_gen, input_cld_subcol_req, & + input_p(idx_chunk_start:idx_chunk_end,:), & + input_t(idx_chunk_start:idx_chunk_end,:), & + input_t_level(idx_chunk_start:idx_chunk_end,:), & + input_d_mass(idx_chunk_start:idx_chunk_end,:), & + input_density(idx_chunk_start:idx_chunk_end,:), & + input_mixing_ratio(idx_chunk_start:idx_chunk_end,:), & + input_o3_mixing_ratio(idx_chunk_start:idx_chunk_end,:), & + input_co2_mixing_ratio(idx_chunk_start:idx_chunk_end,:), & + input_t_surf(idx_chunk_start:idx_chunk_end), & + input_cos_zenith_angle(idx_chunk_start:idx_chunk_end), & + input_solar_irrad(idx_chunk_start:idx_chunk_end), & + input_orog_corr(idx_chunk_start:idx_chunk_end), & + l_planet_grey_surface, & + input_planet_albedo(idx_chunk_start:idx_chunk_end), & + input_planet_emissivity, & + input_layer_heat_capacity(idx_chunk_start:idx_chunk_end,:), & + input_cld_frac(idx_chunk_start:idx_chunk_end,:), & + input_reff_rad(idx_chunk_start:idx_chunk_end,:), & + input_mmr_cl_rad(idx_chunk_start:idx_chunk_end,:), & + soc_flux_direct(idx_chunk_start:idx_chunk_end,:), & + soc_flux_down(idx_chunk_start:idx_chunk_end,:), & + soc_flux_up(idx_chunk_start:idx_chunk_end,:), & + soc_flux_direct_clr(idx_chunk_start:idx_chunk_end,:), & + soc_flux_down_clr(idx_chunk_start:idx_chunk_end,:), & + soc_flux_up_clr(idx_chunk_start:idx_chunk_end,:), & + soc_heating_rate(idx_chunk_start:idx_chunk_end,:), & + soc_spectral_olr(idx_chunk_start:idx_chunk_end,:), & + soc_tot_cloud_cover(idx_chunk_start:idx_chunk_end)) + + else + CALL socrates_calc(Time_diag, control_calc, spectrum_calc, & + n_profile_chunk, n_layer, input_n_cloud_layer, input_n_aer_mode, & + input_cld_subcol_gen, input_cld_subcol_req, & + input_p(idx_chunk_start:idx_chunk_end,:), & + input_t(idx_chunk_start:idx_chunk_end,:), & + input_t_level(idx_chunk_start:idx_chunk_end,:), & + input_d_mass(idx_chunk_start:idx_chunk_end,:), & + input_density(idx_chunk_start:idx_chunk_end,:), & + input_mixing_ratio(idx_chunk_start:idx_chunk_end,:), & + input_o3_mixing_ratio(idx_chunk_start:idx_chunk_end,:), & + input_co2_mixing_ratio(idx_chunk_start:idx_chunk_end,:), & + input_t_surf(idx_chunk_start:idx_chunk_end), & + input_cos_zenith_angle(idx_chunk_start:idx_chunk_end), & + input_solar_irrad(idx_chunk_start:idx_chunk_end), & + input_orog_corr(idx_chunk_start:idx_chunk_end), & + l_planet_grey_surface, & + input_planet_albedo(idx_chunk_start:idx_chunk_end), & + input_planet_emissivity, & + input_layer_heat_capacity(idx_chunk_start:idx_chunk_end,:), & + input_cld_frac(idx_chunk_start:idx_chunk_end,:), & + input_reff_rad(idx_chunk_start:idx_chunk_end,:), & + input_mmr_cl_rad(idx_chunk_start:idx_chunk_end,:), & + soc_flux_direct(idx_chunk_start:idx_chunk_end,:), & + soc_flux_down(idx_chunk_start:idx_chunk_end,:), & + soc_flux_up(idx_chunk_start:idx_chunk_end,:), & + soc_flux_direct_clr(idx_chunk_start:idx_chunk_end,:), & + soc_flux_down_clr(idx_chunk_start:idx_chunk_end,:), & + soc_flux_up_clr(idx_chunk_start:idx_chunk_end,:), & + soc_heating_rate(idx_chunk_start:idx_chunk_end,:)) + endif + + ENDDO + + ! Set output arrays + output_flux_up(:,:,:) = reshape(soc_flux_up(:,:),(/si,sj,sk+1 /)) + output_flux_down(:,:,:) = reshape(soc_flux_down(:,:),(/si,sj,sk+1 /)) + + output_flux_up_clr(:,:,:) = reshape(soc_flux_up_clr(:,:),(/si,sj,sk+1 /)) + output_flux_down_clr(:,:,:) = reshape(soc_flux_down_clr(:,:),(/si,sj,sk+1 /)) + + if(present(output_flux_direct)) then + output_flux_direct(:,:,:) = reshape(soc_flux_direct(:,:),(/si,sj,sk+1 /)) + endif + + if(present(output_flux_direct_clr)) then + output_flux_direct_clr(:,:,:) = reshape(soc_flux_direct_clr(:,:),(/si,sj,sk+1 /)) + endif + + output_heating_rate(:,:,:) = reshape(soc_heating_rate(:,:),(/si,sj,sk /)) + + if(present(tot_cloud_cover)) then + tot_cloud_cover(:,:) = reshape(soc_tot_cloud_cover(:), (/si,sj/)) + endif + + if (soc_lw_mode .eqv. .TRUE.) then + output_soc_spectral_olr(:,:,:) = reshape(soc_spectral_olr(:,:),(/si,sj,int(n_soc_bands_lw,i_def) /)) + endif + end subroutine socrates_interface subroutine run_socrates(Time, Time_diag, rad_lat, rad_lon, temp_in, q_in, t_surf_in, p_full_in, p_half_in, z_full_in, z_half_in, albedo_in, & - temp_tend, net_surf_sw_down, surf_lw_down, delta_t) + temp_tend, net_surf_sw_down, surf_lw_down, delta_t, do_cloud_simple, do_cloud_spookie, cf_rad, reff_rad, qcl_rad) use astronomy_mod, only: diurnal_solar use constants_mod, only: pi, wtmco2, wtmozone, rdgas, gas_constant use interpolator_mod,only: interpolator - USE socrates_config_mod + USE socrates_config_mod ! Input time type(time_type), intent(in) :: Time, Time_diag @@ -716,347 +882,171 @@ subroutine run_socrates(Time, Time_diag, rad_lat, rad_lon, temp_in, q_in, t_surf real, intent(in), dimension(:,:,:) :: temp_in, p_full_in, q_in, z_full_in real, intent(in), dimension(:,:,:) :: p_half_in, z_half_in real, intent(inout), dimension(:,:,:) :: temp_tend - real, intent(out), dimension(:,:) :: net_surf_sw_down, surf_lw_down + real, intent(out), dimension(:,:) :: net_surf_sw_down, surf_lw_down real, intent(in) :: delta_t + logical, intent(in) :: do_cloud_simple, do_cloud_spookie + real, intent(in), dimension(:,:,:) :: cf_rad, reff_rad, qcl_rad integer(i_def) :: n_profile, n_layer real(r_def), dimension(size(temp_in,1), size(temp_in,2)) :: t_surf_for_soc, rad_lat_soc, rad_lon_soc, albedo_soc - real(r_def), dimension(size(temp_in,1), size(temp_in,2), size(temp_in,3)) :: tg_tmp_soc, q_soc, ozone_soc, co2_soc, p_full_soc, output_heating_rate_sw, output_heating_rate_lw, output_heating_rate_total, z_full_soc - real(r_def), dimension(size(temp_in,1), size(temp_in,2), size(temp_in,3)+1) :: p_half_soc, t_half_out, z_half_soc,output_soc_flux_sw_down, output_soc_flux_sw_up, output_soc_flux_lw_down, output_soc_flux_lw_up + real(r_def), dimension(size(temp_in,1), & + size(temp_in,2), size(temp_in,3)) :: tg_tmp_soc, q_soc, ozone_soc, co2_soc, p_full_soc, & + output_heating_rate_sw, output_heating_rate_lw, output_heating_rate_total, & + output_heating_rate_sw_clr, output_heating_rate_lw_clr, & + z_full_soc, cld_frac_soc, reff_rad_soc, mmr_cl_rad_soc, qcl_rad_soc, & + cld_frac_soc_clr, reff_rad_soc_clr, mmr_cl_rad_soc_clr + real(r_def), dimension(size(temp_in,1), & + size(temp_in,2), size(temp_in,3)+1) :: p_half_soc, t_half_out, z_half_soc, output_soc_flux_sw_down, & + output_soc_flux_sw_up, output_soc_flux_lw_down, output_soc_flux_lw_up, & + output_soc_flux_sw_down_clr, output_soc_flux_sw_up_clr, & + output_soc_flux_lw_down_clr, output_soc_flux_lw_up_clr, & + t_half_out_clr + real(r_def), dimension(size(temp_in,1), size(temp_in,2)) :: tot_cloud_cover logical :: soc_lw_mode, used integer :: seconds, days, year_in_s - real :: r_seconds, r_days, r_total_seconds, frac_of_day, frac_of_year, gmt, time_since_ae, rrsun, dt_rad_radians, day_in_s, r_solday, r_dt_rad_avg - real, dimension(size(temp_in,1), size(temp_in,2)) :: coszen, fracsun, surf_lw_net, olr, toa_sw, p2, toa_sw_down, surf_sw_down + real :: r_seconds, r_days, r_total_seconds, frac_of_day, frac_of_year, gmt, time_since_ae, rrsun, & + dt_rad_radians, day_in_s, r_solday, r_dt_rad_avg + real, dimension(size(temp_in,1), size(temp_in,2)) :: coszen, fracsun, surf_lw_net, olr, toa_sw, & + p2, toa_sw_down, surf_sw_down, & + olr_clr, toa_sw_clr, toa_sw_up, toa_sw_up_clr, & + net_surf_sw_down_clr, surf_sw_down_clr, surf_lw_net_clr, surf_lw_down_clr real, dimension(size(temp_in,1), size(temp_in,2), size(temp_in,3)) :: ozone_in, co2_in - real, dimension(size(temp_in,1), size(temp_in,2), size(temp_in,3)+1) :: thd_sw_flux_net, thd_lw_flux_net + real, dimension(size(temp_in,1), size(temp_in,2), size(temp_in,3)+1) :: thd_sw_flux_net, thd_lw_flux_net, thd_sw_flux_clr_net, thd_lw_flux_clr_net + type(time_type) :: Time_loc - + !check if we really want to recompute radiation + ! alarm + call get_time(Time,seconds,days) + r_days = real(days) + r_seconds = real(seconds) + r_total_seconds=r_seconds+(r_days*86400.) - !check if we really want to recompute radiation - ! alarm - call get_time(Time,seconds,days) - r_days = real(days) - r_seconds = real(seconds) - r_total_seconds=r_seconds+(r_days*86400.) - if(r_total_seconds - dt_last .ge. dt_rad) then - dt_last = r_total_seconds - else - if(store_intermediate_rad) then - !required for computation - output_heating_rate_sw = tdt_soc_sw_store - output_heating_rate_lw = tdt_soc_lw_store - net_surf_sw_down = real(net_surf_sw_down_store) - surf_lw_down = real(surf_lw_down_store) - - !only required for output - if (id_soc_surf_flux_lw > 0) then - surf_lw_net = real(surf_lw_net_store) - endif - - if (id_soc_flux_lw > 0) then - thd_lw_flux_net = thd_lw_flux_net_store - endif - - if (id_soc_flux_sw > 0) then - thd_sw_flux_net = thd_sw_flux_net_store - endif - - if (id_soc_surf_flux_sw_down > 0) then - surf_sw_down = surf_sw_down_store - endif - - if (id_soc_olr > 0) then - olr = olr_store - endif - - if (id_soc_toa_sw > 0) then - toa_sw = toa_sw_store - endif - - if (id_soc_toa_sw_down > 0) then - toa_sw_down = toa_sw_down_store - endif - - if (id_soc_coszen > 0) then - coszen = coszen_store - endif - - if (id_soc_ozone > 0) then - ozone_in = thd_ozone_store - endif - - if (id_soc_co2 > 0) then - co2_in = thd_co2_store - endif - - if (id_soc_spectral_olr > 0) then - outputted_soc_spectral_olr = spectral_olr_store - endif - else - output_heating_rate_sw = 0. - output_heating_rate_lw = 0. - thd_sw_flux_net = 0. - thd_lw_flux_net = 0. - net_surf_sw_down = 0. - surf_lw_down = 0. - surf_lw_net = 0. - toa_sw = 0. - olr = 0. - coszen = 0. - ozone_in = 0. - co2_in = 0. - outputted_soc_spectral_olr = 0. - endif - - temp_tend(:,:,:) = temp_tend(:,:,:) + real(output_heating_rate_sw)+real(output_heating_rate_lw) - output_heating_rate_total = output_heating_rate_sw +output_heating_rate_lw - - ! Send diagnostics - if(id_soc_tdt_lw > 0) then - used = send_data ( id_soc_tdt_lw, output_heating_rate_lw, Time_diag) + if(r_total_seconds - dt_last .ge. dt_rad) then + dt_last = r_total_seconds + else + if(store_intermediate_rad) then + !required for computation + output_heating_rate_sw = tdt_soc_sw_store + output_heating_rate_lw = tdt_soc_lw_store + net_surf_sw_down = real(net_surf_sw_down_store) + surf_lw_down = real(surf_lw_down_store) + + !only required for output + if (id_soc_surf_flux_lw > 0) then + surf_lw_net = real(surf_lw_net_store) endif - if(id_soc_tdt_sw > 0) then - used = send_data ( id_soc_tdt_sw, output_heating_rate_sw, Time_diag) + if (id_soc_flux_lw > 0) then + thd_lw_flux_net = thd_lw_flux_net_store endif - if(id_soc_tdt_rad > 0) then - used = send_data ( id_soc_tdt_rad, output_heating_rate_total, Time_diag) - endif - if(id_soc_surf_flux_lw > 0) then - used = send_data ( id_soc_surf_flux_lw, surf_lw_net, Time_diag) + + if (id_soc_flux_sw > 0) then + thd_sw_flux_net = thd_sw_flux_net_store endif - if(id_soc_surf_flux_lw_down > 0) then - used = send_data ( id_soc_surf_flux_lw_down, surf_lw_down, Time_diag) + + if (id_soc_surf_flux_sw_down > 0) then + surf_sw_down = surf_sw_down_store endif - if(id_soc_surf_flux_sw > 0) then - used = send_data ( id_soc_surf_flux_sw, net_surf_sw_down, Time_diag) + + if (id_soc_olr > 0) then + olr = olr_store endif - if(id_soc_surf_flux_sw_down > 0) then - used = send_data ( id_soc_surf_flux_sw_down, surf_sw_down, Time_diag) + + if (id_soc_toa_sw > 0) then + toa_sw = toa_sw_store endif - if(id_soc_olr > 0) then - used = send_data ( id_soc_olr, olr, Time_diag) + + if (id_soc_toa_sw_up > 0) then + toa_sw_up = toa_sw_up_store endif - if(id_soc_toa_sw > 0) then - used = send_data ( id_soc_toa_sw, toa_sw, Time_diag) + + if (id_soc_flux_lw_clr > 0) then + thd_lw_flux_clr_net = thd_lw_flux_clr_net_store endif - if(id_soc_toa_sw_down > 0) then - used = send_data ( id_soc_toa_sw_down, toa_sw_down, Time_diag) + + if (id_soc_flux_sw_clr > 0) then + thd_sw_flux_clr_net = thd_sw_flux_clr_net_store endif - if(id_soc_flux_lw > 0) then - used = send_data ( id_soc_flux_lw, thd_lw_flux_net, Time_diag) + + if (id_soc_olr_clr > 0) then + olr_clr = olr_clr_store endif - if(id_soc_flux_sw > 0) then - used = send_data ( id_soc_flux_sw, thd_sw_flux_net, Time_diag) + + if (id_soc_toa_sw_clr > 0) then + toa_sw_clr = toa_sw_clr_store endif - if(id_soc_coszen > 0) then - used = send_data ( id_soc_coszen, coszen, Time_diag) + if (id_soc_toa_sw_up_clr > 0) then + toa_sw_up_clr = toa_sw_up_clr_store endif - if(id_soc_co2 > 0) then - used = send_data ( id_soc_co2, co2_in, Time_diag) - endif - if(id_soc_ozone > 0) then - used = send_data ( id_soc_ozone, ozone_in, Time_diag) + + if (id_soc_surf_flux_sw_clr > 0) then + net_surf_sw_down_clr = net_surf_sw_down_clr_store endif - if(id_soc_spectral_olr > 0) then - used = send_data ( id_soc_spectral_olr, outputted_soc_spectral_olr, Time_diag) - endif - ! Diagnostics sent - - return !not time yet - - endif - - - !make sure we run perpetual when solday > 0) - if(solday > 0)then - Time_loc = set_time(seconds,solday) - else - Time_loc = Time - endif - - !Set tide-locked flux if tidally-locked = .true. Else use diurnal-solar - !to calculate insolation from orbit! - if (tidally_locked.eq..true.) then - coszen = COS(rad_lat(:,:))*COS(rad_lon(:,:)) - WHERE (coszen < 0.0) coszen = 0.0 - rrsun = 1 ! needs to be set, set to 1 so that stellar_radiation is unchanged in socrates_interface - - elseif (frierson_solar_rad .eq. .true.) then - p2 = (1. - 3.*sin(rad_lat(:,:))**2)/4. - coszen = 0.25 * (1.0 + del_sol * p2 + del_sw * sin(rad_lat(:,:))) - rrsun = 1 ! needs to be set, set to 1 so that stellar_radiation is unchanged in socrates_interface - - else - - ! compute zenith angle - call get_time(Time_loc, seconds, days) - call get_time(length_of_year(), year_in_s) - day_in_s = length_of_day() - - r_seconds=real(seconds) - r_days=real(days) - r_total_seconds=r_seconds+(r_days*86400.) - - frac_of_day = r_total_seconds / day_in_s - - if(solday > 0) then - r_solday=real(solday) - frac_of_year=(r_solday*day_in_s)/year_in_s - else - frac_of_year = r_total_seconds / year_in_s - endif - gmt = abs(mod(frac_of_day, 1.0)) * 2.0 * pi - time_since_ae = modulo(frac_of_year-equinox_day, 1.0) * 2.0 * pi - - if(do_rad_time_avg) then - r_dt_rad_avg=real(dt_rad_avg) - dt_rad_radians = (r_dt_rad_avg/day_in_s)*2.0*pi - call diurnal_solar(rad_lat, rad_lon, gmt, time_since_ae, coszen, fracsun, rrsun, dt_rad_radians) - else - ! Seasonal Cycle: Use astronomical parameters to calculate insolation - call diurnal_solar(rad_lat, rad_lon, gmt, time_since_ae, coszen, fracsun, rrsun) - end if - - endif - - ozone_in = 0.0 - - !get ozone - if(do_read_ozone)then - call interpolator( o3_interp, Time_diag, p_half_in, ozone_in, trim(ozone_field_name)) - if (input_o3_file_is_mmr==.false.) then - ozone_in = ozone_in * wtmozone / (1000. * gas_constant / rdgas ) !Socrates expects all abundances to be mass mixing ratio. So if input file is volume mixing ratio, it must be converted to mass mixing ratio using the molar masses of dry air and ozone - ! Molar mass of dry air calculated from gas_constant / rdgas, and converted into g/mol from kg/mol by multiplying by 1000. This conversion is necessary because wtmozone is in g/mol. - - endif - endif - - if (input_co2_mmr==.false.) then - co2_in = co2_ppmv * 1.e-6 * wtmco2 / (1000. * gas_constant / rdgas )!Convert co2_ppmv to a mass mixing ratio, as required by socrates - ! Molar mass of dry air calculated from gas_constant / rdgas, and converted into g/mol from kg/mol by multiplying by 1000. This conversion is necessary because wtmco2 is in g/mol. - else - co2_in = co2_ppmv * 1.e-6 !No need to convert if it is already a mmr - endif - - !get co2 - if(do_read_co2)then - call interpolator( co2_interp, Time_diag, p_half_in, co2_in, trim(co2_field_name)) - if (input_co2_mmr==.false.) then - co2_in = co2_in * 1.e-6 * wtmco2 / (1000. * gas_constant / rdgas ) - ! Molar mass of dry air calculated from gas_constant / rdgas, and converted into g/mol from kg/mol by multiplying by 1000. This conversion is necessary because wtmco2 is in g/mol. - - endif - endif - - - n_profile = INT(size(temp_in,2)*size(temp_in,1), kind(i_def)) - n_layer = INT(size(temp_in,3), kind(i_def)) - t_surf_for_soc = REAL(t_surf_in(:,:), kind(r_def)) - - ! LW calculation - ! Retrieve output_heating_rate, and downward surface SW and LW fluxes - soc_lw_mode = .TRUE. - - rad_lat_soc = REAL(rad_lat, kind(r_def)) - rad_lon_soc = REAL(rad_lon, kind(r_def)) - tg_tmp_soc = REAL(temp_in, kind(r_def)) - q_soc = REAL(q_in, kind(r_def)) - ozone_soc = REAL(ozone_in, kind(r_def)) - co2_soc = REAL(co2_in, kind(r_def)) - p_full_soc = REAL(p_full_in, kind(r_def)) - p_half_soc = REAL(p_half_in, kind(r_def)) - albedo_soc = REAL(albedo_in, kind(r_def)) - z_full_soc = REAL(z_full_in, kind(r_def)) - z_half_soc = REAL(z_half_in, kind(r_def)) - - CALL socrates_interface(Time, rad_lat_soc, rad_lon_soc, soc_lw_mode, & - tg_tmp_soc, q_soc, ozone_soc, co2_soc, t_surf_for_soc, p_full_soc, p_half_soc, z_full_soc, z_half_soc, albedo_soc, coszen, rrsun, n_profile, n_layer, & - output_heating_rate_lw, output_soc_flux_lw_down, output_soc_flux_lw_up, output_soc_spectral_olr = outputted_soc_spectral_olr, t_half_level_out = t_half_out) - - tg_tmp_soc = tg_tmp_soc + output_heating_rate_lw*delta_t !Output heating rate in K/s, so is a temperature tendency - surf_lw_down(:,:) = REAL(output_soc_flux_lw_down(:,:, n_layer+1)) - surf_lw_net(:,:) = REAL(output_soc_flux_lw_up(:,:,n_layer+1) - output_soc_flux_lw_down(:,:, n_layer+1)) - olr(:,:) = REAL(output_soc_flux_lw_up(:,:,1)) - thd_lw_flux_net = REAL(output_soc_flux_lw_up - output_soc_flux_lw_down) - - temp_tend(:,:,:) = temp_tend(:,:,:) + real(output_heating_rate_lw) - - ! SW calculation - ! Retrieve output_heating_rate, and downward surface SW and LW fluxes - soc_lw_mode = .FALSE. - CALL socrates_interface(Time, rad_lat_soc, rad_lon_soc, soc_lw_mode, & - tg_tmp_soc, q_soc, ozone_soc, co2_soc, t_surf_for_soc, p_full_soc, p_half_soc, z_full_soc, z_half_soc, albedo_soc, coszen, rrsun, n_profile, n_layer, & - output_heating_rate_sw, output_soc_flux_sw_down, output_soc_flux_sw_up) - - tg_tmp_soc = tg_tmp_soc + output_heating_rate_sw*delta_t !Output heating rate in K/s, so is a temperature tendency - net_surf_sw_down(:,:) = REAL(output_soc_flux_sw_down(:,:, n_layer+1)-output_soc_flux_sw_up(:,:,n_layer+1) ) - surf_sw_down(:,:) = REAL(output_soc_flux_sw_down(:,:, n_layer+1)) - toa_sw(:,:) = REAL(output_soc_flux_sw_down(:,:,1)-output_soc_flux_sw_up(:,:,1)) - toa_sw_down(:,:) = REAL(output_soc_flux_sw_down(:,:,1)) - thd_sw_flux_net = REAL(output_soc_flux_sw_up - output_soc_flux_sw_down) - - - temp_tend(:,:,:) = temp_tend(:,:,:) + real(output_heating_rate_sw) - - output_heating_rate_total = output_heating_rate_lw + output_heating_rate_sw - - if(store_intermediate_rad)then - ! required for calculation - tdt_soc_lw_store = output_heating_rate_lw - tdt_soc_sw_store = output_heating_rate_sw - net_surf_sw_down_store = real(net_surf_sw_down, kind(r_def)) - surf_lw_down_store = real(surf_lw_down, kind(r_def)) - - ! required for output - if (id_soc_surf_flux_lw > 0) then - surf_lw_net_store = real(surf_lw_net, kind(r_def)) - endif - - if (id_soc_flux_lw > 0) then - thd_lw_flux_net_store = thd_lw_flux_net - endif - - if (id_soc_flux_sw > 0) then - thd_sw_flux_net_store = thd_sw_flux_net + + if (id_soc_surf_flux_sw_down_clr > 0) then + surf_sw_down_clr = surf_sw_down_clr_store endif - - if (id_soc_surf_flux_sw_down > 0) then - surf_sw_down_store = surf_sw_down - endif - if (id_soc_toa_sw_down > 0) then - toa_sw_down_store = toa_sw_down - endif + if (id_soc_surf_flux_lw_clr > 0) then + surf_lw_net_clr = surf_lw_net_clr_store + endif - if (id_soc_olr > 0) then - olr_store = olr - endif + if (id_soc_surf_flux_lw_down_clr > 0) then + surf_lw_down_clr = surf_lw_down_clr_store + endif - if (id_soc_toa_sw > 0) then - toa_sw_store = toa_sw - endif + if (id_soc_toa_sw_down > 0) then + toa_sw_down = toa_sw_down_store + endif - if (id_soc_coszen > 0) then - coszen_store = coszen - endif + if (id_soc_coszen > 0) then + coszen = coszen_store + endif - if (id_soc_ozone > 0) then - thd_ozone_store = ozone_in - endif + if (id_soc_ozone > 0) then + ozone_in = thd_ozone_store + endif - if (id_soc_co2 > 0) then - thd_co2_store = co2_in + if (id_soc_co2 > 0) then + co2_in = thd_co2_store endif - + if (id_soc_spectral_olr > 0) then - spectral_olr_store = outputted_soc_spectral_olr - endif - - endif + outputted_soc_spectral_olr = spectral_olr_store + endif + else + output_heating_rate_sw = 0. + output_heating_rate_lw = 0. + thd_sw_flux_net = 0. + thd_lw_flux_net = 0. + net_surf_sw_down = 0. + surf_lw_down = 0. + surf_lw_net = 0. + toa_sw = 0. + olr = 0. + toa_sw_up = 0. + + olr_clr = 0. + toa_sw_clr = 0. + toa_sw_up_clr = 0. + thd_sw_flux_clr_net = 0. + thd_lw_flux_clr_net = 0. + net_surf_sw_down_clr = 0. + surf_sw_down_clr = 0. + surf_lw_net_clr = 0. + surf_lw_down_clr = 0. + + coszen = 0. + ozone_in = 0. + co2_in = 0. + outputted_soc_spectral_olr = 0. + endif + + temp_tend(:,:,:) = temp_tend(:,:,:) + real(output_heating_rate_sw)+real(output_heating_rate_lw) + output_heating_rate_total = output_heating_rate_sw +output_heating_rate_lw ! Send diagnostics if(id_soc_tdt_lw > 0) then @@ -1067,10 +1057,13 @@ subroutine run_socrates(Time, Time_diag, rad_lat, rad_lon, temp_in, q_in, t_surf endif if(id_soc_tdt_rad > 0) then used = send_data ( id_soc_tdt_rad, output_heating_rate_total, Time_diag) - endif + endif if(id_soc_surf_flux_lw > 0) then used = send_data ( id_soc_surf_flux_lw, surf_lw_net, Time_diag) endif + if(id_soc_surf_flux_lw_down > 0) then + used = send_data ( id_soc_surf_flux_lw_down, surf_lw_down, Time_diag) + endif if(id_soc_surf_flux_sw > 0) then used = send_data ( id_soc_surf_flux_sw, net_surf_sw_down, Time_diag) endif @@ -1086,79 +1079,479 @@ subroutine run_socrates(Time, Time_diag, rad_lat, rad_lon, temp_in, q_in, t_surf if(id_soc_toa_sw_down > 0) then used = send_data ( id_soc_toa_sw_down, toa_sw_down, Time_diag) endif + if(id_soc_toa_sw_up > 0) then + used = send_data ( id_soc_toa_sw_up, toa_sw_up, Time_diag) + endif + + if(id_soc_olr_clr > 0) then + used = send_data ( id_soc_olr_clr, olr_clr, Time_diag) + endif + if(id_soc_toa_sw_clr > 0) then + used = send_data ( id_soc_toa_sw_clr, toa_sw_clr, Time_diag) + endif + if(id_soc_toa_sw_up_clr > 0) then + used = send_data ( id_soc_toa_sw_up_clr, toa_sw_up_clr, Time_diag) + endif + if(id_soc_flux_lw_clr > 0) then + used = send_data ( id_soc_flux_lw_clr, thd_lw_flux_clr_net, Time_diag) + endif + if(id_soc_flux_sw_clr > 0) then + used = send_data ( id_soc_flux_sw_clr, thd_sw_flux_clr_net, Time_diag) + endif + if (id_soc_surf_flux_sw_clr > 0) then + used = send_data ( id_soc_surf_flux_sw_clr, net_surf_sw_down_clr, Time_diag) + endif + if (id_soc_surf_flux_sw_down_clr > 0) then + used = send_data ( id_soc_surf_flux_sw_down_clr, surf_sw_down_clr, Time_diag) + endif + if (id_soc_surf_flux_lw_clr > 0) then + used = send_data ( id_soc_surf_flux_lw_clr, surf_lw_net_clr, Time_diag) + endif + if (id_soc_surf_flux_lw_down_clr > 0) then + used = send_data ( id_soc_surf_flux_lw_down_clr, surf_lw_down_clr, Time_diag) + endif + if(id_soc_flux_lw > 0) then used = send_data ( id_soc_flux_lw, thd_lw_flux_net, Time_diag) endif - if(id_soc_surf_flux_lw_down > 0) then - used = send_data ( id_soc_surf_flux_lw_down, surf_lw_down, Time_diag) - endif if(id_soc_flux_sw > 0) then used = send_data ( id_soc_flux_sw, thd_sw_flux_net, Time_diag) endif + if(id_soc_coszen > 0) then used = send_data ( id_soc_coszen, coszen, Time_diag) endif - if(id_soc_co2 > 0) then + if(id_soc_co2 > 0) then used = send_data ( id_soc_co2, co2_in, Time_diag) - endif - if(id_soc_ozone > 0) then + endif + if(id_soc_ozone > 0) then used = send_data ( id_soc_ozone, ozone_in, Time_diag) - endif - if(id_soc_spectral_olr > 0) then + endif + if(id_soc_spectral_olr > 0) then used = send_data ( id_soc_spectral_olr, outputted_soc_spectral_olr, Time_diag) - endif - ! Diagnostics sent + endif + ! Diagnostics sent + + return !not time yet + + endif + + !make sure we run perpetual when solday > 0) + if(solday > 0)then + Time_loc = set_time(seconds,solday) + else + Time_loc = Time + endif + + !Set tide-locked flux if tidally-locked = .true. Else use diurnal-solar + !to calculate insolation from orbit! + if (tidally_locked.eqv..true.) then + coszen = COS(rad_lat(:,:))*COS(rad_lon(:,:)) + WHERE (coszen < 0.0) coszen = 0.0 + rrsun = 1 ! needs to be set, set to 1 so that stellar_radiation is unchanged in socrates_interface + + elseif (frierson_solar_rad .eqv. .true.) then + p2 = (1. - 3.*sin(rad_lat(:,:))**2)/4. + coszen = 0.25 * (1.0 + del_sol * p2 + del_sw * sin(rad_lat(:,:))) + rrsun = 1 ! needs to be set, set to 1 so that stellar_radiation is unchanged in socrates_interface + + else + ! compute zenith angle + call get_time(Time_loc, seconds, days) + call get_time(length_of_year(), year_in_s) + day_in_s = length_of_day() + + r_seconds=real(seconds) + r_days=real(days) + r_total_seconds=r_seconds+(r_days*86400.) + + frac_of_day = r_total_seconds / day_in_s + + if(solday > 0) then + r_solday=real(solday) + frac_of_year=(r_solday*day_in_s)/year_in_s + else + frac_of_year = r_total_seconds / year_in_s + endif + gmt = abs(mod(frac_of_day, 1.0)) * 2.0 * pi + time_since_ae = modulo(frac_of_year-equinox_day, 1.0) * 2.0 * pi + + if(do_rad_time_avg) then + r_dt_rad_avg=real(dt_rad_avg) + dt_rad_radians = (r_dt_rad_avg/day_in_s)*2.0*pi + call diurnal_solar(rad_lat, rad_lon, gmt, time_since_ae, coszen, fracsun, rrsun, dt_rad_radians) + else + ! Seasonal Cycle: Use astronomical parameters to calculate insolation + call diurnal_solar(rad_lat, rad_lon, gmt, time_since_ae, coszen, fracsun, rrsun) + end if + + endif + + ozone_in = 0.0 + + !get ozone + if(do_read_ozone)then + call interpolator( o3_interp, Time_diag, p_half_in, ozone_in, trim(ozone_field_name)) + endif + if(do_scm_ozone)then ! Allows for option to specify ozone vertical profile in namelist for SCM. + if(do_read_ozone)then + call error_mesg('socrates_interface', 'Cannot set do_scm_ozone and do_read_ozone = .true.', FATAL) + endif + if((size(temp_in,1)>1).or.(size(temp_in,2)>1))then + call error_mesg('socrates_interface', 'Cannot set do_scm_ozone if simulating more than one column, use do_read_ozone instead', FATAL) + endif + if(scm_ozone(size(temp_in,3)).eq.-1)then + call error_mesg('socrates_interface', 'Input o3 must be specified on model pressure levels but not enough levels specified', FATAL) + endif + if(scm_ozone(size(temp_in,3)+1).ne.-1)then + call error_mesg('socrates_interface', 'Input o3 must be specified on model pressure levels but too many levels specified', FATAL) + endif + ozone_in(1,1,:) = scm_ozone(1:size(temp_in,3)) + !PUT THIS WARNING SOMEWHERE ELSE + endif + if (do_read_ozone .or. do_scm_ozone) then + if (input_o3_file_is_mmr.eqv..false.) then + + ozone_in = ozone_in * wtmozone / (1000. * gas_constant / rdgas ) !Socrates expects all abundances to be mass mixing ratio. So if input file is volume mixing ratio, it must be converted to mass mixing ratio using the molar masses of dry air and ozone + ! Molar mass of dry air calculated from gas_constant / rdgas, and converted into g/mol from kg/mol by multiplying by 1000. This conversion is necessary because wtmozone is in g/mol. + + endif + endif + + if (input_co2_mmr .eqv. .false.) then + co2_in = co2_ppmv * 1.e-6 * wtmco2 / (1000. * gas_constant / rdgas ) + ! Convert co2_ppmv to a mass mixing ratio, as required by socrates + ! Molar mass of dry air calculated from gas_constant / rdgas, and converted into g/mol + ! from kg/mol by multiplying by 1000. This conversion is necessary because wtmco2 is in g/mol. + else + co2_in = co2_ppmv * 1.e-6 !No need to convert if it is already a mmr + endif + + !get co2 + if(do_read_co2)then + call interpolator( co2_interp, Time_diag, p_half_in, co2_in, trim(co2_field_name)) + if (input_co2_mmr .eqv. .false.) then + co2_in = co2_in * 1.e-6 * wtmco2 / (1000. * gas_constant / rdgas ) + ! Molar mass of dry air calculated from gas_constant / rdgas, and converted into g/mol + ! from kg/mol by multiplying by 1000. This conversion is necessary because wtmco2 is in g/mol. + endif + endif + + if(do_cloud_simple .or. do_cloud_spookie) then + cld_frac_soc = REAL(cf_rad, kind(r_def)) + reff_rad_soc = REAL(reff_rad, kind(r_def)) + + qcl_rad_soc = REAL(qcl_rad, kind(r_def)) + mmr_cl_rad_soc = qcl_rad_soc / (1.0 - qcl_rad_soc) !check if qcl is indeed specific humidity and not mmr + else + cld_frac_soc = 0. + reff_rad_soc = 0. + mmr_cl_rad_soc = 0. + endif + + cld_frac_soc_clr = 0. + reff_rad_soc_clr = 0. + mmr_cl_rad_soc_clr = 0. + + n_profile = INT(size(temp_in,2)*size(temp_in,1), kind(i_def)) + n_layer = INT(size(temp_in,3), kind(i_def)) + t_surf_for_soc = REAL(t_surf_in(:,:), kind(r_def)) + ! LW calculation + ! Retrieve output_heating_rate, and downward surface SW and LW fluxes + soc_lw_mode = .TRUE. + + rad_lat_soc = REAL(rad_lat, kind(r_def)) + rad_lon_soc = REAL(rad_lon, kind(r_def)) + tg_tmp_soc = REAL(temp_in, kind(r_def)) + q_soc = REAL(q_in, kind(r_def)) + ozone_soc = REAL(ozone_in, kind(r_def)) + co2_soc = REAL(co2_in, kind(r_def)) + p_full_soc = REAL(p_full_in, kind(r_def)) + p_half_soc = REAL(p_half_in, kind(r_def)) + albedo_soc = REAL(albedo_in, kind(r_def)) + z_full_soc = REAL(z_full_in, kind(r_def)) + z_half_soc = REAL(z_half_in, kind(r_def)) + + CALL socrates_interface(Time, rad_lat_soc, rad_lon_soc, soc_lw_mode, & + tg_tmp_soc, q_soc, ozone_soc, co2_soc, t_surf_for_soc, p_full_soc, & + p_half_soc, z_full_soc, z_half_soc, albedo_soc, coszen, rrsun, & + n_profile, n_layer, cld_frac_soc, reff_rad_soc, mmr_cl_rad_soc, & + output_heating_rate_lw, output_soc_flux_lw_down, output_soc_flux_lw_up, & + output_soc_flux_lw_down_clr, output_soc_flux_lw_up_clr, & + do_cloud_simple, do_cloud_spookie, & + !optional outs + output_soc_spectral_olr = outputted_soc_spectral_olr, & + t_half_level_out = t_half_out, & + tot_cloud_cover = tot_cloud_cover ) + + tg_tmp_soc = tg_tmp_soc + output_heating_rate_lw*delta_t !Output heating rate in K/s, so is a temperature tendency + surf_lw_down(:,:) = REAL(output_soc_flux_lw_down(:,:, n_layer+1)) + surf_lw_net(:,:) = REAL(output_soc_flux_lw_up(:,:,n_layer+1) - output_soc_flux_lw_down(:,:, n_layer+1)) + olr(:,:) = REAL(output_soc_flux_lw_up(:,:,1)) + thd_lw_flux_net = REAL(output_soc_flux_lw_up - output_soc_flux_lw_down) + + olr_clr(:,:) = REAL(output_soc_flux_lw_up_clr(:,:,1)) + thd_lw_flux_clr_net = REAL(output_soc_flux_lw_up_clr - output_soc_flux_lw_down_clr) + surf_lw_down_clr(:,:) = REAL(output_soc_flux_lw_down_clr(:,:, n_layer+1)) + surf_lw_net_clr(:,:) = REAL(output_soc_flux_lw_up_clr(:,:,n_layer+1) - output_soc_flux_lw_down_clr(:,:, n_layer+1)) + + temp_tend(:,:,:) = temp_tend(:,:,:) + real(output_heating_rate_lw) + + ! SW calculation + ! Retrieve output_heating_rate, and downward surface SW and LW fluxes + soc_lw_mode = .FALSE. + CALL socrates_interface(Time, rad_lat_soc, rad_lon_soc, soc_lw_mode, & + tg_tmp_soc, q_soc, ozone_soc, co2_soc, t_surf_for_soc, p_full_soc, & + p_half_soc, z_full_soc, z_half_soc, albedo_soc, coszen, rrsun, & + n_profile, n_layer, cld_frac_soc, reff_rad_soc, mmr_cl_rad_soc, & + output_heating_rate_sw, output_soc_flux_sw_down, output_soc_flux_sw_up, & + output_soc_flux_sw_down_clr, output_soc_flux_sw_up_clr, & + do_cloud_simple, do_cloud_spookie) + + tg_tmp_soc = tg_tmp_soc + output_heating_rate_sw*delta_t !Output heating rate in K/s, so is a temperature tendency + net_surf_sw_down(:,:) = REAL(output_soc_flux_sw_down(:,:, n_layer+1)-output_soc_flux_sw_up(:,:,n_layer+1) ) + surf_sw_down(:,:) = REAL(output_soc_flux_sw_down(:,:, n_layer+1)) + toa_sw(:,:) = REAL(output_soc_flux_sw_down(:,:,1)-output_soc_flux_sw_up(:,:,1)) + toa_sw_down(:,:) = REAL(output_soc_flux_sw_down(:,:,1)) + thd_sw_flux_net = REAL(output_soc_flux_sw_up - output_soc_flux_sw_down) + toa_sw_up(:,:) = REAL(output_soc_flux_sw_up(:,:,1)) + + toa_sw_clr(:,:) = REAL(output_soc_flux_sw_down_clr(:,:,1)-output_soc_flux_sw_up_clr(:,:,1)) + thd_sw_flux_clr_net = REAL(output_soc_flux_sw_up_clr - output_soc_flux_sw_down_clr) + toa_sw_up_clr(:,:) = REAL(output_soc_flux_sw_up_clr(:,:,1)) + net_surf_sw_down_clr(:,:) = REAL(output_soc_flux_sw_down_clr(:,:, n_layer+1)-output_soc_flux_sw_up_clr(:,:,n_layer+1) ) + surf_sw_down_clr(:,:) = REAL(output_soc_flux_sw_down_clr(:,:, n_layer+1)) + + temp_tend(:,:,:) = temp_tend(:,:,:) + real(output_heating_rate_sw) + output_heating_rate_total = output_heating_rate_lw + output_heating_rate_sw + + if(store_intermediate_rad)then + ! required for calculation + tdt_soc_lw_store = output_heating_rate_lw + tdt_soc_sw_store = output_heating_rate_sw + net_surf_sw_down_store = real(net_surf_sw_down, kind(r_def)) + surf_lw_down_store = real(surf_lw_down, kind(r_def)) + + ! required for output + if (id_soc_surf_flux_lw > 0) then + surf_lw_net_store = real(surf_lw_net, kind(r_def)) + endif + + if (id_soc_flux_lw > 0) then + thd_lw_flux_net_store = thd_lw_flux_net + endif + + if (id_soc_flux_sw > 0) then + thd_sw_flux_net_store = thd_sw_flux_net + endif + + if (id_soc_surf_flux_sw_down > 0) then + surf_sw_down_store = surf_sw_down + endif -end subroutine run_socrates + if (id_soc_toa_sw_down > 0) then + toa_sw_down_store = toa_sw_down + endif + + if (id_soc_olr > 0) then + olr_store = olr + endif + + if (id_soc_toa_sw > 0) then + toa_sw_store = toa_sw + endif + + if (id_soc_toa_sw_up > 0) then + toa_sw_up_store = toa_sw_up + endif + + if (id_soc_flux_lw_clr > 0) then + thd_lw_flux_clr_net_store = thd_lw_flux_clr_net + endif + + if (id_soc_flux_sw_clr > 0) then + thd_sw_flux_clr_net_store = thd_sw_flux_clr_net + endif + + if (id_soc_olr_clr > 0) then + olr_clr_store = olr_clr + endif + + if (id_soc_toa_sw_clr > 0) then + toa_sw_clr_store = toa_sw_clr + endif + + if (id_soc_toa_sw_up_clr > 0) then + toa_sw_up_clr_store = toa_sw_up_clr + endif + + if (id_soc_surf_flux_sw_clr > 0) then + net_surf_sw_down_clr_store = net_surf_sw_down_clr + endif + + if (id_soc_surf_flux_sw_down_clr > 0) then + surf_sw_down_clr_store = surf_sw_down_clr + endif + + if (id_soc_surf_flux_lw_clr > 0) then + surf_lw_net_clr_store = surf_lw_net_clr + endif + + if (id_soc_surf_flux_lw_down_clr > 0) then + surf_lw_down_clr_store = surf_lw_down_clr + endif + + if (id_soc_coszen > 0) then + coszen_store = coszen + endif + + if (id_soc_ozone > 0) then + thd_ozone_store = ozone_in + endif + + if (id_soc_co2 > 0) then + thd_co2_store = co2_in + endif + + if (id_soc_spectral_olr > 0) then + spectral_olr_store = outputted_soc_spectral_olr + endif + + endif + + ! Send diagnostics + if(id_soc_tdt_lw > 0) then + used = send_data ( id_soc_tdt_lw, output_heating_rate_lw, Time_diag) + endif + if(id_soc_tdt_sw > 0) then + used = send_data ( id_soc_tdt_sw, output_heating_rate_sw, Time_diag) + endif + if(id_soc_tdt_rad > 0) then + used = send_data ( id_soc_tdt_rad, output_heating_rate_total, Time_diag) + endif + if(id_soc_surf_flux_lw > 0) then + used = send_data ( id_soc_surf_flux_lw, surf_lw_net, Time_diag) + endif + if(id_soc_surf_flux_sw > 0) then + used = send_data ( id_soc_surf_flux_sw, net_surf_sw_down, Time_diag) + endif + if(id_soc_surf_flux_sw_down > 0) then + used = send_data ( id_soc_surf_flux_sw_down, surf_sw_down, Time_diag) + endif + if(id_soc_olr > 0) then + used = send_data ( id_soc_olr, olr, Time_diag) + endif + if(id_soc_toa_sw > 0) then + used = send_data ( id_soc_toa_sw, toa_sw, Time_diag) + endif + if(id_soc_toa_sw_down > 0) then + used = send_data ( id_soc_toa_sw_down, toa_sw_down, Time_diag) + endif + if(id_soc_toa_sw_up > 0) then + used = send_data ( id_soc_toa_sw_up, toa_sw_up, Time_diag) + endif + + if(id_soc_olr_clr > 0) then + used = send_data ( id_soc_olr_clr, olr_clr, Time_diag) + endif + if(id_soc_toa_sw_clr > 0) then + used = send_data ( id_soc_toa_sw_clr, toa_sw_clr, Time_diag) + endif + if(id_soc_toa_sw_up_clr > 0) then + used = send_data ( id_soc_toa_sw_up_clr, toa_sw_up_clr, Time_diag) + endif + if(id_soc_flux_lw_clr > 0) then + used = send_data ( id_soc_flux_lw_clr, thd_lw_flux_clr_net, Time_diag) + endif + if(id_soc_flux_sw_clr > 0) then + used = send_data ( id_soc_flux_sw_clr, thd_sw_flux_clr_net, Time_diag) + endif + if (id_soc_surf_flux_sw_clr > 0) then + used = send_data ( id_soc_surf_flux_sw_clr, net_surf_sw_down_clr, Time_diag) + endif + if (id_soc_surf_flux_sw_down_clr > 0) then + used = send_data ( id_soc_surf_flux_sw_down_clr, surf_sw_down_clr, Time_diag) + endif + if (id_soc_surf_flux_lw_clr > 0) then + used = send_data ( id_soc_surf_flux_lw_clr, surf_lw_net_clr, Time_diag) + endif + if (id_soc_surf_flux_lw_down_clr > 0) then + used = send_data ( id_soc_surf_flux_lw_down_clr, surf_lw_down_clr, Time_diag) + endif + if(id_soc_flux_lw > 0) then + used = send_data ( id_soc_flux_lw, thd_lw_flux_net, Time_diag) + endif + if(id_soc_surf_flux_lw_down > 0) then + used = send_data ( id_soc_surf_flux_lw_down, surf_lw_down, Time_diag) + endif + if(id_soc_flux_sw > 0) then + used = send_data ( id_soc_flux_sw, thd_sw_flux_net, Time_diag) + endif + if(id_soc_coszen > 0) then + used = send_data ( id_soc_coszen, coszen, Time_diag) + endif + if(id_soc_co2 > 0) then + used = send_data ( id_soc_co2, co2_in, Time_diag) + endif + if(id_soc_ozone > 0) then + used = send_data ( id_soc_ozone, ozone_in, Time_diag) + endif + if(id_soc_spectral_olr > 0) then + used = send_data ( id_soc_spectral_olr, outputted_soc_spectral_olr, Time_diag) + endif + if(id_soc_tot_cloud_cover > 0) then + used = send_data ( id_soc_tot_cloud_cover, tot_cloud_cover*1e2, Time_diag) + endif + ! Diagnostics sent + +end subroutine run_socrates subroutine run_socrates_end - use interpolator_mod, only: interpolator_end - USE socrates_config_mod + use interpolator_mod, only: interpolator_end + USE socrates_config_mod if(do_read_ozone) call interpolator_end(o3_interp) if(do_read_co2) call interpolator_end(co2_interp) - end subroutine run_socrates_end !***************************************************************************************** - subroutine interp_temp(z_full,z_half,temp_in, t_half) - implicit none - - real(r_def),dimension(:,:),intent(in) :: z_full,z_half,temp_in - real(r_def),dimension(size(z_half,1), size(z_half,2)),intent(out) :: t_half - - integer i,k,kend - real dzk,dzk1,dzk2 - -! note: z_full(kend) = z_half(kend), so there's something fishy -! also, for some reason, z_half(k=1)=0. so we need to deal with k=1 separately - kend=size(z_full,2) - do k=2,kend - do i=1,size(temp_in,1) - dzk2 = 1./( z_full(i,k-1) - z_full(i,k) ) - dzk = ( z_half(i,k ) - z_full(i,k) )*dzk2 - dzk1 = ( z_full(i,k-1) - z_half(i,k) )*dzk2 - t_half(i,k) = temp_in(i,k)*dzk1 + temp_in(i,k-1)*dzk - enddo - enddo -! top of the atmosphere: need to extrapolate. z_half(1)=0, so need to use values -! on full grid - do i=1,size(temp_in,1) - !standard linear extrapolation - !top: use full points, and distance is 1.5 from k=2 - t_half(i,1) = 0.5*(3*temp_in(i,1)-temp_in(i,2)) - !bottom: z=0 => distance is - !-z_full(kend-1)/(z_full(kend)-z_full(kend-1)) - t_half(i,kend+1) = temp_in(i,kend-1) & - + (z_half(i,kend+1) - z_full(i,kend-1))& - * (temp_in(i,kend ) - temp_in(i,kend-1))& - / (z_full(i,kend ) - z_full(i,kend-1)) - enddo - - - end subroutine interp_temp +subroutine interp_temp(z_full,z_half,temp_in, t_half) + implicit none + + real(r_def),dimension(:,:),intent(in) :: z_full,z_half,temp_in + real(r_def),dimension(size(z_half,1), size(z_half,2)),intent(out) :: t_half + + integer i,k,kend + real dzk,dzk1,dzk2 + + ! note: z_full(kend) = z_half(kend), so there's something fishy + ! also, for some reason, z_half(k=1)=0. so we need to deal with k=1 separately + kend=size(z_full,2) + do k=2,kend + do i=1,size(temp_in,1) + dzk2 = 1./( z_full(i,k-1) - z_full(i,k) ) + dzk = ( z_half(i,k ) - z_full(i,k) )*dzk2 + dzk1 = ( z_full(i,k-1) - z_half(i,k) )*dzk2 + t_half(i,k) = temp_in(i,k)*dzk1 + temp_in(i,k-1)*dzk + enddo + enddo + ! top of the atmosphere: need to extrapolate. z_half(1)=0, so need to use values + ! on full grid + do i=1,size(temp_in,1) + !standard linear extrapolation + !top: use full points, and distance is 1.5 from k=2 + t_half(i,1) = 0.5*(3*temp_in(i,1)-temp_in(i,2)) + !bottom: z=0 => distance is + !-z_full(kend-1)/(z_full(kend)-z_full(kend-1)) + t_half(i,kend+1) = temp_in(i,kend-1) & + + (z_half(i,kend+1) - z_full(i,kend-1)) & + * (temp_in(i,kend ) - temp_in(i,kend-1)) & + / (z_full(i,kend ) - z_full(i,kend-1)) + enddo + +end subroutine interp_temp !***************************************************************************************** - + end module socrates_interface_mod diff --git a/src/atmos_param/socrates/interface/socrates_set_cld.F90 b/src/atmos_param/socrates/interface/socrates_set_cld.F90 new file mode 100644 index 000000000..3af8423b8 --- /dev/null +++ b/src/atmos_param/socrates/interface/socrates_set_cld.F90 @@ -0,0 +1,503 @@ +! *****************************COPYRIGHT******************************* +! (C) Crown copyright Met Office. All rights reserved. +! For further details please refer to the file COPYRIGHT.txt +! which you should have received as part of this distribution. +! *****************************COPYRIGHT******************************* +! +! Set the variables in the Socrates cloud type +! +!------------------------------------------------------------------------------ +module socrates_set_cld +implicit none +character(len=*), parameter, private :: ModuleName = 'SOCRATES_SET_CLD' +contains + +subroutine set_cld(cld, control, dimen, spectrum, n_profile, n_layer, & + cloud_frac, conv_frac, & + liq_frac, ice_frac, liq_conv_frac, ice_conv_frac, & + liq_mmr, ice_mmr, liq_conv_mmr, ice_conv_mmr, & + liq_dim, ice_dim, liq_conv_dim, ice_conv_dim, & + dp_corr_strat, dp_corr_conv) + +use def_cld, only: StrCld, allocate_cld, allocate_cld_prsc +use def_control, only: StrCtrl +use def_dimen, only: StrDim +use def_spectrum, only: StrSpecData +use soc_constants_mod, only: i_def, r_def +use rad_pcf, only: & + ip_cloud_homogen, ip_cloud_ice_water, ip_cloud_conv_strat, ip_cloud_csiw, & + ip_clcmp_st_water, ip_clcmp_st_ice, ip_clcmp_cnv_water, ip_clcmp_cnv_ice, & + ip_phase_water, ip_phase_ice, ip_cloud_type_homogen, & + ip_cloud_type_water, ip_cloud_type_ice, & + ip_cloud_type_strat, ip_cloud_type_conv, & + ip_cloud_type_sw, ip_cloud_type_si, ip_cloud_type_cw, ip_cloud_type_ci, & + ip_drop_unparametrized, ip_ice_unparametrized, i_normal, i_err_fatal + +use fms_mod, only: error_mesg, FATAL + +implicit none + + +! Cloud properties: +type(StrCld), intent(out) :: cld + +! Control options: +type(StrCtrl), intent(in) :: control + +! Dimensions: +type(StrDim), intent(in) :: dimen + +! Spectral data: +type(StrSpecData), intent(in) :: spectrum + +integer(i_def), intent(in) :: n_profile +integer(i_def), intent(in) :: n_layer + +real(r_def), intent(in), optional :: & + cloud_frac(:,:), conv_frac(:,:), & + liq_frac(:,:), ice_frac(:,:), liq_conv_frac(:,:), ice_conv_frac(:,:), & + liq_mmr(:,:), ice_mmr(:,:), liq_conv_mmr(:,:), ice_conv_mmr(:,:), & + liq_dim(:,:), ice_dim(:,:), liq_conv_dim(:,:), ice_conv_dim(:,:) +! Liquid and ice cloud fractions, gridbox mean mixing ratios, and +! effective dimensions + +real(r_def), intent(in), optional :: dp_corr_strat, dp_corr_conv +! Decorrelation pressure scales for cloud vertical overlap + + +! Local variables +integer :: i, j, k, l +! Loop variables +integer :: i_phase, i_param_type, n_cloud_parameter +! Working variables +integer :: i_cloud_type(dimen%nd_cloud_component) +! Types of cloud to which each component contributes + +real(r_def) :: condensed_min_dim +real(r_def) :: condensed_max_dim +! Minimum and maximum dimensions of each condensed component + +real(r_def) :: eps = EPSILON(1.0) +real(r_def) :: min_cloud_fraction = 0.0001 + +integer :: ierr = i_normal +character (len=*), parameter :: RoutineName = 'SET_CLD' +character (len=128) :: cmessage + +! Functions called +integer, external :: set_n_cloud_parameter + + +! Allocate structure for the core radiation code interface +call allocate_cld(cld, dimen, spectrum) +call allocate_cld_prsc(cld, dimen, spectrum) + +if (.not.control%l_cloud) then + return +end if + +!------------------------------------------------------------------------------ +! Set properties of condensed components +!------------------------------------------------------------------------------ + +if (control%l_ice .and. control%l_drop) then + select case (control%i_cloud_representation) + case (ip_cloud_homogen, ip_cloud_ice_water) + cld%n_condensed = 2 + cld%type_condensed(1) = ip_clcmp_st_water + cld%type_condensed(2) = ip_clcmp_st_ice + case (ip_cloud_conv_strat, ip_cloud_csiw) + cld%n_condensed = 4 + cld%type_condensed(1) = ip_clcmp_st_water + cld%type_condensed(2) = ip_clcmp_st_ice + cld%type_condensed(3) = ip_clcmp_cnv_water + cld%type_condensed(4) = ip_clcmp_cnv_ice + end select +else if (control%l_ice .and. .not.control%l_drop) then + select case (control%i_cloud_representation) + case (ip_cloud_homogen, ip_cloud_ice_water) + cld%n_condensed = 1 + cld%type_condensed(1) = ip_clcmp_st_ice + case (ip_cloud_conv_strat, ip_cloud_csiw) + cld%n_condensed = 2 + cld%type_condensed(1) = ip_clcmp_st_ice + cld%type_condensed(2) = ip_clcmp_cnv_ice + end select +else if (.not.control%l_ice .and. control%l_drop) then + select case (control%i_cloud_representation) + case (ip_cloud_homogen, ip_cloud_ice_water) + cld%n_condensed = 1 + cld%type_condensed(1) = ip_clcmp_st_water + case (ip_cloud_conv_strat, ip_cloud_csiw) + cld%n_condensed = 2 + cld%type_condensed(1) = ip_clcmp_st_water + cld%type_condensed(2) = ip_clcmp_cnv_water + end select +else + cmessage = 'Cloud on, but no condensed components included.' + ierr=i_err_fatal +! CALL ereport(ModuleName//':'//RoutineName, ierr, cmessage) + call error_mesg(ModuleName,cmessage, FATAL) +end if + +do i=1, cld%n_condensed + select case (cld%type_condensed(i)) + case (ip_clcmp_st_water) + i_phase = ip_phase_water + i_param_type = control%i_st_water + case (ip_clcmp_st_ice) + i_phase = ip_phase_ice + i_param_type = control%i_st_ice + case (ip_clcmp_cnv_water) + i_phase = ip_phase_water + i_param_type = control%i_cnv_water + case (ip_clcmp_cnv_ice) + i_phase = ip_phase_ice + i_param_type = control%i_cnv_ice + end select + + select case (i_phase) + case (ip_phase_water) + if (i_param_type <= 0) then + cld%i_condensed_param(i) = ip_drop_unparametrized + cmessage = 'Prescribed liquid cloud not yet implemented.' + ierr=i_err_fatal + ! CALL ereport(ModuleName//':'//RoutineName, ierr, cmessage) + call error_mesg(ModuleName,cmessage, FATAL) + else if (i_param_type > spectrum%dim%nd_drop_type) then + cmessage = 'Liquid cloud type outside allowed range.' + ierr=i_err_fatal + ! CALL ereport(ModuleName//':'//RoutineName, ierr, cmessage) + call error_mesg(ModuleName,cmessage, FATAL) + else if (spectrum%drop%l_drop_type(i_param_type)) then + ! Take parametrisation from spectral file + cld%i_condensed_param(i) = spectrum%drop%i_drop_parm(i_param_type) + cld%condensed_n_phf(i) = spectrum%drop%n_phf(i_param_type) + ! DEPENDS ON: set_n_cloud_parameter + n_cloud_parameter = set_n_cloud_parameter( cld%i_condensed_param(i), & + cld%type_condensed(i), cld%condensed_n_phf(i) ) + do j=1, spectrum%basic%n_band + do k=1, n_cloud_parameter + cld%condensed_param_list(k, i, j) & + = spectrum%drop%parm_list(k, j, i_param_type) + end do + end do + ! Assign droplet mass mixing ratio and effective radius + condensed_min_dim = spectrum%drop%parm_min_dim(i_param_type) + condensed_max_dim = spectrum%drop%parm_max_dim(i_param_type) + select case (cld%type_condensed(i)) + case (ip_clcmp_st_water) + if (present(liq_mmr).and.present(liq_dim)) then + do k = dimen%id_cloud_top, n_layer + do l = 1, n_profile + cld%condensed_mix_ratio(l, k, i) = liq_mmr(l, k) + cld%condensed_dim_char(l, k, i) = min( max( liq_dim(l, k), & + condensed_min_dim ), condensed_max_dim ) + end do + end do + else + cmessage = 'Liquid MMR and effective radius not provided.' + ierr=i_err_fatal + ! CALL ereport(ModuleName//':'//RoutineName, ierr, cmessage) + call error_mesg(ModuleName,cmessage, FATAL) + end if + case (ip_clcmp_cnv_water) + if (present(liq_conv_mmr).and.present(liq_conv_dim)) then + do k = dimen%id_cloud_top, n_layer + do l = 1, n_profile + cld%condensed_mix_ratio(l, k, i) = liq_conv_mmr(l, k) + cld%condensed_dim_char(l, k, i) = min( max( liq_conv_dim(l, k), & + condensed_min_dim ), condensed_max_dim ) + end do + end do + else + cmessage = 'Convective liquid MMR and effective radius not provided.' + ierr=i_err_fatal + ! CALL ereport(ModuleName//':'//RoutineName, ierr, cmessage) + call error_mesg(ModuleName,cmessage, FATAL) + end if + end select + else + cmessage = 'Liquid cloud type not in spectral file.' + ierr=i_err_fatal + ! CALL ereport(ModuleName//':'//RoutineName, ierr, cmessage) + call error_mesg(ModuleName,cmessage, FATAL) + end if + case (ip_phase_ice) + if (i_param_type <= 0) then + cld%i_condensed_param(i) = ip_ice_unparametrized + cmessage = 'Prescribed ice cloud not yet implemented.' + ierr=i_err_fatal + ! CALL ereport(ModuleName//':'//RoutineName, ierr, cmessage) + call error_mesg(ModuleName,cmessage, FATAL) + else if (i_param_type > spectrum%dim%nd_ice_type) then + cmessage = 'Ice cloud type outside allowed range.' + ierr=i_err_fatal + ! CALL ereport(ModuleName//':'//RoutineName, ierr, cmessage) + call error_mesg(ModuleName,cmessage, FATAL) + else if (spectrum%ice%l_ice_type(i_param_type)) then + ! Take parametrisation from spectral file + cld%i_condensed_param(i) = spectrum%ice%i_ice_parm(i_param_type) + cld%condensed_n_phf(i) = spectrum%ice%n_phf(i_param_type) + n_cloud_parameter = set_n_cloud_parameter( cld%i_condensed_param(i), & + cld%type_condensed(i), cld%condensed_n_phf(i) ) + do j=1, spectrum%basic%n_band + do k=1, n_cloud_parameter + cld%condensed_param_list(k, i, j) & + = spectrum%ice%parm_list(k, j, i_param_type) + end do + end do + ! Assign ice mass mixing ratio and effective dimension + condensed_min_dim = spectrum%ice%parm_min_dim(i_param_type) + condensed_max_dim = spectrum%ice%parm_max_dim(i_param_type) + select case (cld%type_condensed(i)) + case (ip_clcmp_st_ice) + if (present(ice_mmr).and.present(ice_dim)) then + do k = dimen%id_cloud_top, n_layer + do l = 1, n_profile + cld%condensed_mix_ratio(l, k, i) = ice_mmr(l, k) + cld%condensed_dim_char(l, k, i) = min( max( ice_dim(l, k), & + condensed_min_dim ), condensed_max_dim ) + end do + end do + else + cmessage = 'Ice MMR and effective radius not provided.' + ierr=i_err_fatal + ! CALL ereport(ModuleName//':'//RoutineName, ierr, cmessage) + call error_mesg(ModuleName,cmessage, FATAL) + end if + case (ip_clcmp_cnv_ice) + if (present(ice_conv_mmr).and.present(ice_conv_dim)) then + do k = dimen%id_cloud_top, n_layer + do l = 1, n_profile + cld%condensed_mix_ratio(l, k, i) = ice_conv_mmr(l, k) + cld%condensed_dim_char(l, k, i) = min( max( ice_conv_dim(l, k), & + condensed_min_dim ), condensed_max_dim ) + end do + end do + else + cmessage = 'Convective ice MMR and effective radius not provided.' + ierr=i_err_fatal + ! CALL ereport(ModuleName//':'//RoutineName, ierr, cmessage) + call error_mesg(ModuleName,cmessage, FATAL) + end if + end select + else + cmessage = 'Ice cloud type not in spectral file.' + ierr=i_err_fatal + ! CALL ereport(ModuleName//':'//RoutineName, ierr, cmessage) + call error_mesg(ModuleName,cmessage, FATAL) + end if + end select +end do + +! Set the decorrelation scalings for cloud vertical overlap +if (present(dp_corr_strat)) then + cld%dp_corr_strat = dp_corr_strat +else + cld%dp_corr_strat = 0.0_r_def +end if +if (present(dp_corr_conv)) then + cld%dp_corr_conv = dp_corr_conv +else + cld%dp_corr_conv = 0.0_r_def +end if + + +!------------------------------------------------------------------------------ +! Set cloud amounts and convert mixing ratios to in-cloud values +!------------------------------------------------------------------------------ + +! Set cloud fractions +select case (control%i_cloud_representation) +case (ip_cloud_homogen) + cld%n_cloud_type = 1 + do i = 1, cld%n_condensed + i_cloud_type(i) = ip_cloud_type_homogen + end do + if (present(cloud_frac)) then + do k = dimen%id_cloud_top, n_layer + do l = 1, n_profile + cld%frac_cloud(l, k, ip_cloud_type_homogen) = cloud_frac(l, k) + end do + end do + else + cmessage = 'Cloud fraction not provided.' + ierr=i_err_fatal + ! CALL ereport(ModuleName//':'//RoutineName, ierr, cmessage) + call error_mesg(ModuleName,cmessage, FATAL) + end if +case (ip_cloud_ice_water) + cld%n_cloud_type = 2 + do i = 1, cld%n_condensed + select case (cld%type_condensed(i)) + case (ip_clcmp_st_water) + i_cloud_type(i) = ip_cloud_type_water + case (ip_clcmp_st_ice) + i_cloud_type(i) = ip_cloud_type_ice + end select + end do + if (present(liq_frac).and.present(ice_frac).and.present(cloud_frac)) then + do k = dimen%id_cloud_top, n_layer + do l = 1, n_profile + if (liq_frac(l, k) + ice_frac(l, k) > eps) then + ! Split mixed phase fraction between ice and liquid + cld%frac_cloud(l, k, ip_cloud_type_water) = & + cloud_frac(l, k)*liq_frac(l, k) / (liq_frac(l, k)+ice_frac(l, k)) + cld%frac_cloud(l, k, ip_cloud_type_ice) = & + cloud_frac(l, k)*ice_frac(l, k) / (liq_frac(l, k)+ice_frac(l, k)) + else + cld%frac_cloud(l, k, ip_cloud_type_water) = 0.0_r_def + cld%frac_cloud(l, k, ip_cloud_type_ice) = 0.0_r_def + end if + end do + end do + else if (present(liq_frac).and.present(ice_frac)) then + do k = dimen%id_cloud_top, n_layer + do l = 1, n_profile + cld%frac_cloud(l, k, ip_cloud_type_water) = liq_frac(l, k) + cld%frac_cloud(l, k, ip_cloud_type_ice) = ice_frac(l, k) + end do + end do + else + cmessage = 'Liquid and ice cloud fractions not provided.' + ierr=i_err_fatal + ! CALL ereport(ModuleName//':'//RoutineName, ierr, cmessage) + call error_mesg(ModuleName,cmessage, FATAL) + end if +case (ip_cloud_conv_strat) + cld%n_cloud_type = 2 + do i = 1, cld%n_condensed + select case (cld%type_condensed(i)) + case (ip_clcmp_st_water) + i_cloud_type(i) = ip_cloud_type_strat + case (ip_clcmp_st_ice) + i_cloud_type(i) = ip_cloud_type_strat + case (ip_clcmp_cnv_water) + i_cloud_type(i) = ip_cloud_type_conv + case (ip_clcmp_cnv_ice) + i_cloud_type(i) = ip_cloud_type_conv + end select + end do + if (present(cloud_frac).and.present(conv_frac)) then + do k = dimen%id_cloud_top, n_layer + do l = 1, n_profile + cld%frac_cloud(l, k, ip_cloud_type_strat) = cloud_frac(l, k) + cld%frac_cloud(l, k, ip_cloud_type_conv) = conv_frac(l, k) + end do + end do + else + cmessage = 'Cloud fraction and convective cloud fraction not provided.' + ierr=i_err_fatal + ! CALL ereport(ModuleName//':'//RoutineName, ierr, cmessage) + call error_mesg(ModuleName,cmessage, FATAL) + end if +case (ip_cloud_csiw) + cld%n_cloud_type = 4 + do i = 1, cld%n_condensed + select case (cld%type_condensed(i)) + case (ip_clcmp_st_water) + i_cloud_type(i) = ip_cloud_type_sw + case (ip_clcmp_st_ice) + i_cloud_type(i) = ip_cloud_type_si + case (ip_clcmp_cnv_water) + i_cloud_type(i) = ip_cloud_type_cw + case (ip_clcmp_cnv_ice) + i_cloud_type(i) = ip_cloud_type_ci + end select + end do + if (present(liq_frac).and.present(ice_frac).and.present(cloud_frac)) then + do k = dimen%id_cloud_top, n_layer + do l = 1, n_profile + if (liq_frac(l, k) + ice_frac(l, k) > eps) then + ! Split mixed phase fraction between ice and liquid + cld%frac_cloud(l, k, ip_cloud_type_sw) = & + cloud_frac(l, k)*liq_frac(l, k) / (liq_frac(l, k)+ice_frac(l, k)) + cld%frac_cloud(l, k, ip_cloud_type_si) = & + cloud_frac(l, k)*ice_frac(l, k) / (liq_frac(l, k)+ice_frac(l, k)) + else + cld%frac_cloud(l, k, ip_cloud_type_sw) = 0.0_r_def + cld%frac_cloud(l, k, ip_cloud_type_si) = 0.0_r_def + end if + end do + end do + else if (present(liq_frac).and.present(ice_frac)) then + do k = dimen%id_cloud_top, n_layer + do l = 1, n_profile + cld%frac_cloud(l, k, ip_cloud_type_sw) = liq_frac(l, k) + cld%frac_cloud(l, k, ip_cloud_type_si) = ice_frac(l, k) + end do + end do + else + cmessage = 'Liquid and ice cloud fractions not provided.' + ierr=i_err_fatal + ! CALL ereport(ModuleName//':'//RoutineName, ierr, cmessage) + call error_mesg(ModuleName,cmessage, FATAL) + end if + if (present(liq_conv_frac).and.present(ice_conv_frac).and. & + present(conv_frac)) then + do k = dimen%id_cloud_top, n_layer + do l = 1, n_profile + if (liq_conv_frac(l, k) + ice_conv_frac(l, k) > eps) then + ! Split mixed phase fraction between ice and liquid + cld%frac_cloud(l, k, ip_cloud_type_cw) = conv_frac(l, k) & + *liq_conv_frac(l, k) / (liq_conv_frac(l, k)+ice_conv_frac(l, k)) + cld%frac_cloud(l, k, ip_cloud_type_ci) = conv_frac(l, k) & + *ice_conv_frac(l, k) / (liq_conv_frac(l, k)+ice_conv_frac(l, k)) + else + cld%frac_cloud(l, k, ip_cloud_type_cw) = 0.0_r_def + cld%frac_cloud(l, k, ip_cloud_type_ci) = 0.0_r_def + end if + end do + end do + else if (present(liq_conv_frac).and.present(ice_conv_frac)) then + do k = dimen%id_cloud_top, n_layer + do l = 1, n_profile + cld%frac_cloud(l, k, ip_cloud_type_cw) = liq_conv_frac(l, k) + cld%frac_cloud(l, k, ip_cloud_type_ci) = ice_conv_frac(l, k) + end do + end do + else + cmessage = 'Liquid and ice convective cloud fractions not provided.' + ierr=i_err_fatal + ! CALL ereport(ModuleName//':'//RoutineName, ierr, cmessage) + call error_mesg(ModuleName,cmessage, FATAL) + end if +end select + +! Convert mass mixing ratios to in-cloud values +do i = 1, cld%n_condensed + do k = dimen%id_cloud_top, n_layer + do l = 1, n_profile + cld%condensed_mix_ratio(l, k, i) = cld%condensed_mix_ratio(l, k, i) & + / max(cld%frac_cloud(l, k, i_cloud_type(i)), eps) + end do + end do +end do + +! Normalise the cloud fractions +do k = dimen%id_cloud_top, n_layer + do l = 1, n_profile + cld%w_cloud(l, k) = sum(cld%frac_cloud(l, k, 1:cld%n_cloud_type)) + if (cld%w_cloud(l, k) > min_cloud_fraction) then + do j=1, cld%n_cloud_type + cld%frac_cloud(l, k, j) = cld%frac_cloud(l, k, j) / cld%w_cloud(l, k) + ! write(6,*) cld%frac_cloud(l, k, j) / cld%w_cloud(l, k), cld%frac_cloud(l, k, j) , cld%w_cloud(l, k), 'div?', cld%n_cloud_type + end do + else + cld%w_cloud(l, k) = 0.0_r_def + cld%frac_cloud(l, k, 1:cld%n_cloud_type) = 0.0_r_def + end if + if (cld%w_cloud(l, k) > 1.0_r_def + min_cloud_fraction) then + cmessage = 'Cloud fraction greater than 1.' + ierr=i_err_fatal + ! CALL ereport(ModuleName//':'//RoutineName, ierr, cmessage) + call error_mesg(ModuleName,cmessage, FATAL) + else if (cld%w_cloud(l, k) > 1.0_r_def) then + cld%w_cloud(l, k) = 1.0_r_def + end if + end do +end do + +end subroutine set_cld +end module socrates_set_cld diff --git a/src/atmos_param/vert_turb_driver/vert_turb_driver.F90 b/src/atmos_param/vert_turb_driver/vert_turb_driver.F90 index 5bdac40ee..01784c0f3 100644 --- a/src/atmos_param/vert_turb_driver/vert_turb_driver.F90 +++ b/src/atmos_param/vert_turb_driver/vert_turb_driver.F90 @@ -144,8 +144,8 @@ subroutine vert_turb_driver (is, js, Time, Time_next, dt, tdtlw, & p_half, p_full, z_half, z_full, u_star, & b_star, q_star, rough, lat, convect, & u, v, t, q, r, um, vm, tm, qm, rm, & - udt, vdt, tdt, qdt, rdt, diff_t, diff_m, & - gust, z_pbl, mask, kbot ) + udt, vdt, tdt, qdt, rdt, ind_lcl, do_lcl_diffusivity_depth, diff_t, diff_m, & + gust, z_pbl, mask, kbot ) !----------------------------------------------------------------------- integer, intent(in) :: is, js @@ -159,6 +159,8 @@ subroutine vert_turb_driver (is, js, Time, Time_next, dt, tdtlw, & u, v, t, q, um, vm, tm, qm, & udt, vdt, tdt, qdt real, intent(in) , dimension(:,:,:,:) :: r, rm, rdt + integer, intent(in), dimension(:,:) :: ind_lcl + logical, intent(in) :: do_lcl_diffusivity_depth real, intent(out), dimension(:,:,:) :: diff_t, diff_m real, intent(out), dimension(:,:) :: gust, z_pbl real, intent(in),optional, dimension(:,:,:) :: mask @@ -306,11 +308,15 @@ subroutine vert_turb_driver (is, js, Time, Time_next, dt, tdtlw, & !--------------------------- !------------------- non-local K scheme -------------- - - call diffusivity ( tt, qq, uu, vv, p_full, p_half, z_full, z_half, & - u_star, b_star, z_pbl, diff_m, diff_t, & - kbot = kbot) - + if (do_lcl_diffusivity_depth) then + call diffusivity ( tt, qq, uu, vv, p_full, p_half, z_full, z_half, & + u_star, b_star, z_pbl, diff_m, diff_t, & + ind_lcl = ind_lcl, kbot = kbot) + else + call diffusivity ( tt, qq, uu, vv, p_full, p_half, z_full, z_half, & + u_star, b_star, z_pbl, diff_m, diff_t, & + kbot = kbot) + endif !--------------------------- else if (do_edt) then !---------------------------- diff --git a/src/atmos_spectral/driver/solo/atmosphere.F90 b/src/atmos_spectral/driver/solo/atmosphere.F90 index 25da9465e..3091e1fe0 100644 --- a/src/atmos_spectral/driver/solo/atmosphere.F90 +++ b/src/atmos_spectral/driver/solo/atmosphere.F90 @@ -32,17 +32,25 @@ module atmosphere_mod use constants_mod, only: grav, pi -use transforms_mod, only: trans_grid_to_spherical, trans_spherical_to_grid, & - get_deg_lon, get_deg_lat, get_grid_boundaries, grid_domain, & - spectral_domain, get_grid_domain, get_lon_max, get_lat_max, atmosphere_domain - use time_manager_mod, only: time_type, get_time, operator( + ) use press_and_geopot_mod, only: compute_pressures_and_heights -use spectral_dynamics_mod, only: spectral_dynamics_init, spectral_dynamics, spectral_dynamics_end, & - get_num_levels, get_axis_id, spectral_diagnostics, get_initial_fields, & - complete_robert_filter, get_surf_geopotential +#ifdef COLUMN_MODEL + use spec_mpp_mod, only: grid_domain, get_grid_domain, atmosphere_domain + use column_mod, only: column_init, column, column_end, & + get_axis_id, column_diagnostics, get_num_levels, & + get_surf_geopotential, get_initial_fields + use column_grid_mod, only: get_deg_lon, get_deg_lat, get_grid_boundaries, & + get_lon_max, get_lat_max +#else + use spectral_dynamics_mod, only: spectral_dynamics_init, spectral_dynamics, spectral_dynamics_end, & + get_num_levels, get_axis_id, spectral_diagnostics, get_initial_fields, & + complete_robert_filter, get_surf_geopotential + use transforms_mod, only: trans_grid_to_spherical, trans_spherical_to_grid, & + get_deg_lon, get_deg_lat, get_grid_boundaries, grid_domain, & + spectral_domain, get_grid_domain, get_lon_max, get_lat_max, atmosphere_domain +#endif use tracer_type_mod, only: tracer_type @@ -78,7 +86,7 @@ module atmosphere_mod integer, parameter :: num_time_levels = 2 integer :: is, ie, js, je, num_levels, num_tracers, nhum -logical :: dry_model +logical :: dry_model, column_model real, allocatable, dimension(:,:,:,:) :: p_half, p_full real, allocatable, dimension(:,:,:,:) :: z_half, z_full @@ -142,7 +150,13 @@ subroutine atmosphere_init(Time_init, Time, Time_step_in) call get_number_tracers(MODEL_ATMOS, num_prog=num_tracers) allocate (tracer_attributes(num_tracers)) -call spectral_dynamics_init(Time, Time_step, tracer_attributes, dry_model, nhum) +#ifdef COLUMN_MODEL + call column_init(Time, Time_step, tracer_attributes, dry_model, nhum) + column_model = .true. +#else + call spectral_dynamics_init(Time, Time_step, tracer_attributes, dry_model, nhum) + column_model = .false. +#endif call get_grid_domain(is, ie, js, je) call get_num_levels(num_levels) @@ -284,7 +298,7 @@ subroutine atmosphere(Time) Time_next = Time + Time_step if(idealized_moist_model) then - call idealized_moist_phys(Time, p_half, p_full, z_half, z_full, ug, vg, tg, grid_tracers, & + call idealized_moist_phys(Time, p_half, p_full, z_half, z_full, ug, vg, psg, wg_full, tg, grid_tracers, & previous, current, dt_ug, dt_vg, dt_tg, dt_tracers) else call hs_forcing(1, ie-is+1, 1, je-js+1, delta_t, Time_next, rad_lon_2d, rad_lat_2d, & @@ -302,11 +316,17 @@ subroutine atmosphere(Time) else future = previous endif - -call spectral_dynamics(Time, psg(:,:,future), ug(:,:,:,future), vg(:,:,:,future), & +#ifdef COLUMN_MODEL +call column(Time, psg(:,:,future), ug(:,:,:,future), vg(:,:,:,future), & tg(:,:,:,future), tracer_attributes, grid_tracers(:,:,:,:,:), future, & dt_psg, dt_ug, dt_vg, dt_tg, dt_tracers, wg_full, & p_full(:,:,:,current), p_half(:,:,:,current), z_full(:,:,:,current)) +#else + call spectral_dynamics(Time, psg(:,:,future), ug(:,:,:,future), vg(:,:,:,future), & + tg(:,:,:,future), tracer_attributes, grid_tracers(:,:,:,:,:), future, & + dt_psg, dt_ug, dt_vg, dt_tg, dt_tracers, wg_full, & + p_full(:,:,:,current), p_half(:,:,:,current), z_full(:,:,:,current)) +#endif if(dry_model) then call compute_pressures_and_heights(tg(:,:,:,future), psg(:,:,future), surf_geopotential, & @@ -317,8 +337,13 @@ subroutine atmosphere(Time) grid_tracers(:,:,:,future,nhum)) endif +#ifdef COLUMN_MODEL +call column_diagnostics(Time_next, psg(:,:,future), ug(:,:,:,future), vg(:,:,:,future), & +tg(:,:,:,future), wg_full, grid_tracers(:,:,:,:,:), future) +#else call spectral_diagnostics(Time_next, psg(:,:,future), ug(:,:,:,future), vg(:,:,:,future), & tg(:,:,:,future), wg_full, grid_tracers(:,:,:,:,:), future) +#endif previous = current current = future @@ -359,7 +384,11 @@ subroutine atmosphere_end else call hs_forcing_end endif +#ifdef COLUMN_MODEL +call column_end(tracer_attributes) +#else call spectral_dynamics_end(tracer_attributes) +#endif deallocate(tracer_attributes) module_is_initialized = .false. diff --git a/src/atmos_spectral/driver/solo/idealized_moist_phys.F90 b/src/atmos_spectral/driver/solo/idealized_moist_phys.F90 index 1d7a89eb2..81963dbc6 100644 --- a/src/atmos_spectral/driver/solo/idealized_moist_phys.F90 +++ b/src/atmos_spectral/driver/solo/idealized_moist_phys.F90 @@ -6,9 +6,11 @@ module idealized_moist_phys_mod use fms_mod, only: open_namelist_file, close_file #endif -use fms_mod, only: write_version_number, file_exist, close_file, stdlog, error_mesg, NOTE, FATAL, read_data, field_size, uppercase, mpp_pe +use fms_mod, only: write_version_number, file_exist, close_file, stdlog, error_mesg, NOTE, & + FATAL, WARNING, read_data, field_size, uppercase, mpp_pe, check_nml_error -use constants_mod, only: grav, rdgas, rvgas, cp_air, PSTD_MKS, dens_h2o !mj cp_air needed for rrtmg !s pstd_mks needed for pref calculation +! cp_air needed for rrtmg and pstd_mks needed for pref calculation +use constants_mod, only: grav, rdgas, rvgas, cp_air, PSTD_MKS, dens_h2o use time_manager_mod, only: time_type, get_time, operator( + ) @@ -18,6 +20,10 @@ module idealized_moist_phys_mod use two_stream_gray_rad_mod, only: two_stream_gray_rad_init, two_stream_gray_rad_down, two_stream_gray_rad_up, two_stream_gray_rad_end +use cloud_simple_mod, only: cloud_simple_init, cloud_simple_end, cloud_simple + +use cloud_spookie_mod, only: cloud_spookie_init, cloud_spookie + use mixed_layer_mod, only: mixed_layer_init, mixed_layer, mixed_layer_end, albedo_calc use lscale_cond_mod, only: lscale_cond_init, lscale_cond, lscale_cond_end @@ -32,21 +38,23 @@ module idealized_moist_phys_mod use diag_manager_mod, only: register_diag_field, send_data -use transforms_mod, only: get_grid_domain - +#ifdef COLUMN_MODEL +use column_mod, only: get_num_levels, get_surf_geopotential, get_axis_id +use spec_mpp_mod, only: get_grid_domain, grid_domain +#else +use transforms_mod, only: get_grid_domain, grid_domain use spectral_dynamics_mod, only: get_axis_id, get_num_levels, get_surf_geopotential +#endif use surface_flux_mod, only: surface_flux, gp_surface_flux -use sat_vapor_pres_mod, only: lookup_es !s Have added this to allow relative humdity to be calculated in a consistent way. +use sat_vapor_pres_mod, only: lookup_es ! needed for relative humdity to be calculated in a consistent way. -use damping_driver_mod, only: damping_driver, damping_driver_init, damping_driver_end !s MiMA uses damping +use damping_driver_mod, only: damping_driver, damping_driver_init, damping_driver_end ! MiMA uses damping use press_and_geopot_mod, only: pressure_variables -use mpp_domains_mod, only: mpp_get_global_domain !s added to enable land reading - -use transforms_mod, only: grid_domain +use mpp_domains_mod, only: mpp_get_global_domain ! needed for reading in land use tracer_manager_mod, only: get_number_tracers, query_method @@ -90,9 +98,10 @@ module idealized_moist_phys_mod logical :: module_is_initialized =.false. logical :: turb = .false. +logical :: do_lcl_diffusivity_depth = .false. logical :: do_virtual = .false. ! whether virtual temp used in gcm_vert_diff -!s Convection scheme options +! Convection scheme options character(len=256) :: convection_scheme = 'unset' !< Use a specific convection scheme. Valid options integer, parameter :: UNSET = -1, & !! are NONE, SIMPLE_BETTS_MILLER, FULL_BETTS_MILLER, DRY NO_CONV = 0, & @@ -100,62 +109,67 @@ module idealized_moist_phys_mod FULL_BETTS_MILLER_CONV = 2,& DRY_CONV = 3, & RAS_CONV = 4 - + integer :: r_conv_scheme = UNSET ! the selected convection scheme logical :: lwet_convection = .false. logical :: do_bm = .false. logical :: do_ras = .false. -!s Radiation options +! Cloud options +logical :: do_cloud_simple = .false. ! SimCloud cloud scheme +logical :: do_cloud_spookie = .false. ! SPOOKIE protocol cloud scheme + +! Radiation options logical :: two_stream_gray = .true. logical :: do_rrtm_radiation = .false. logical :: do_socrates_radiation = .false. -!s MiMA uses damping +! MiMA uses damping logical :: do_damping = .false. logical :: mixed_layer_bc = .false. -logical :: gp_surface = .false. !s Use Schneider & Liu 2009's prescription of lower-boundary heat flux +logical :: gp_surface = .false. ! Use Schneider & Liu 2009's prescription of lower-boundary heat flux -logical :: do_simple = .false. !s Have added this to enable relative humidity to be calculated correctly below. +logical :: do_simple = .false. ! Have added this to enable relative humidity to be calculated correctly below. real :: roughness_heat = 0.05 real :: roughness_moist = 0.05 real :: roughness_mom = 0.05 real :: land_roughness_prefactor = 1.0 -!s options for adding idealised land +! options for adding idealised land character(len=256) :: land_option = 'none' character(len=256) :: land_file_name = 'INPUT/land.nc' character(len=256) :: land_field_name = 'land_mask' -! RG Add bucket -logical :: bucket = .false. +! Add bucket +logical :: bucket = .false. integer :: future real :: init_bucket_depth = 1000. ! default large value -real :: init_bucket_depth_land = 20. +real :: init_bucket_depth_land = 20. real :: max_bucket_depth_land = 0.15 ! default from Manabe 1969 real :: robert_bucket = 0.04 ! default robert coefficient for bucket depth LJJ real :: raw_bucket = 0.53 ! default raw coefficient for bucket depth LJJ -! end RG Add bucket +! end Add bucket namelist / idealized_moist_phys_nml / turb, lwet_convection, do_bm, do_ras, roughness_heat, & + do_cloud_simple, do_cloud_spookie, & two_stream_gray, do_rrtm_radiation, do_damping,& mixed_layer_bc, do_simple, & roughness_moist, roughness_mom, do_virtual, & - land_option, land_file_name, land_field_name, & !s options for idealised land - land_roughness_prefactor, & - gp_surface, convection_scheme, & - bucket, init_bucket_depth, init_bucket_depth_land, & !RG Add bucket + land_option, land_file_name, land_field_name, & ! options for idealised land + land_roughness_prefactor, & + gp_surface, convection_scheme, & + bucket, init_bucket_depth, init_bucket_depth_land, & max_bucket_depth_land, robert_bucket, raw_bucket, & - do_socrates_radiation + do_socrates_radiation, do_lcl_diffusivity_depth -integer, parameter :: num_time_levels = 2 !RG Add bucket - number of time levels added to allow timestepping in this module -real, allocatable, dimension(:,:,:) :: bucket_depth ! RG Add bucket -real, allocatable, dimension(:,: ) :: dt_bucket, filt ! RG Add bucket +integer, parameter :: num_time_levels = 2 ! Add bucket - number of time levels added to allow timestepping in this module +real, allocatable, dimension(:,:,:) :: bucket_depth +real, allocatable, dimension(:,: ) :: dt_bucket, filt real, allocatable, dimension(:,:) :: & z_surf, & ! surface height @@ -166,9 +180,9 @@ module idealized_moist_phys_mod rough_mom, & ! momentum roughness length for surface_flux rough_heat, & ! heat roughness length for surface_flux rough_moist, & ! moisture roughness length for surface_flux - depth_change_lh, & ! tendency in bucket depth due to latent heat transfer ! RG Add bucket - depth_change_cond, & ! tendency in bucket depth due to condensation rain ! RG Add bucket - depth_change_conv, & ! tendency in bucket depth due to convection rain ! RG Add bucket + depth_change_lh, & ! tendency in bucket depth due to latent heat transfer + depth_change_cond, & ! tendency in bucket depth due to condensation rain + depth_change_conv, & ! tendency in bucket depth due to convection rain gust, & ! gustiness constant z_pbl, & ! gustiness constant flux_t, & ! surface sensible heat flux @@ -193,22 +207,23 @@ module idealized_moist_phys_mod dtaudu_atm, & ! d(stress component)/d(atmos wind) fracland, & ! fraction of land in gridbox rough, & ! roughness for vert_turb_driver - albedo, & !s albedo now defined in mixed_layer_init - coszen, & !s make sure this is ready for assignment in run_rrtmg - pbltop, & !s Used as an input to damping_driver, outputted from vert_turb_driver - ex_del_m, & !mp586 for 10m winds and 2m temp - ex_del_h, & !mp586 for 10m winds and 2m temp - ex_del_q, & !mp586 for 10m winds and 2m temp - temp_2m, & !mp586 for 10m winds and 2m temp - u_10m, & !mp586 for 10m winds and 2m temp - v_10m, & !mp586 for 10m winds and 2m temp - q_2m, & ! Add 2m specific humidity - rh_2m ! Add 2m relative humidity + albedo, & ! albedo now defined in mixed_layer_init + coszen, & ! make sure this is ready for assignment in run_rrtmg + pbltop, & ! used as an input to damping_driver, outputted from vert_turb_driver + ex_del_m, & ! used for 10m winds and 2m temp + ex_del_h, & ! used for 10m winds and 2m temp + ex_del_q, & ! used for 10m winds and 2m temp + temp_2m, & ! used for 10m winds and 2m temp + u_10m, & ! used for 10m winds and 2m temp + v_10m, & ! used for 10m winds and 2m temp + q_2m, & ! used for 2m specific humidity + rh_2m ! used for 2m relative humidity real, allocatable, dimension(:,:,:) :: & diff_m, & ! momentum diffusion coeff. diff_t, & ! temperature diffusion coeff. - tdtlw, & ! place holder. appears in calling arguments of vert_turb_driver but not used unless do_edt=.true. -- pjp + tdtlw, & ! place holder. appears in calling arguments of + ! vert_turb_driver but not used unless do_edt=.true. diss_heat, & ! heat dissipated by vertical diffusion diss_heat_ray, & ! heat dissipated by rayleigh bottom drag (used when gp_surface=.True.) non_diff_dt_ug, & ! zonal wind tendency except from vertical diffusion @@ -225,20 +240,26 @@ module idealized_moist_phys_mod avail, & ! generate surf. flux (all true) land, & ! land points (all false) coldT, & ! should precipitation be snow at this point - convect ! place holder. appears in calling arguments of vert_turb_driver but not used unless do_entrain=.true. -- pjp + convect ! place holder. appears in calling arguments of + ! vert_turb_driver but not used unless do_entrain=.true. real, allocatable, dimension(:,:) :: & land_ones ! land points (all zeros) -real, allocatable, dimension(:,:) :: & +integer, allocatable, dimension(:,:) :: & klzbs, & ! stored level of zero buoyancy values + klcls ! stored lifting condensation level values + +real, allocatable, dimension(:,:) :: & cape, & ! convectively available potential energy cin, & ! convective inhibition (this and the above are before the adjustment) invtau_q_relaxation, & ! temperature relaxation time scale invtau_t_relaxation, & ! humidity relaxation time scale rain, & ! Can be resolved or parameterised snow, & ! - precip ! cumulus rain + resolved rain + resolved snow + precip, & ! cumulus rain + resolved rain + resolved snow + convective_rain ! save the result for convective rain + real, allocatable, dimension(:,:,:) :: & t_ref, & ! relaxation temperature for bettsmiller scheme @@ -260,34 +281,34 @@ module idealized_moist_phys_mod id_conv_dt_qg, & ! temperature tendency from convection id_cond_dt_tg, & ! temperature tendency from condensation id_cond_dt_qg, & ! temperature tendency from condensation - id_bucket_depth, & ! bucket depth variable for output - RG Add bucket - id_bucket_depth_conv, & ! bucket depth variation induced by convection - RG Add bucket - id_bucket_depth_cond, & ! bucket depth variation induced by condensation - RG Add bucket - id_bucket_depth_lh, & ! bucket depth variation induced by LH - RG Add bucket - id_rh, & ! Relative humidity + id_bucket_depth, & ! bucket depth variable for output + id_bucket_depth_conv, & ! bucket depth variation induced by convection + id_bucket_depth_cond, & ! bucket depth variation induced by condensation + id_bucket_depth_lh, & ! bucket depth variation induced by LH + id_rh, & ! Relative humidity id_diss_heat_ray,& ! Heat dissipated by rayleigh bottom drag if gp_surface=.True. id_z_tg, & ! Relative humidity id_cape, & - id_cin, & + id_cin, & id_flux_u, & ! surface flux of zonal mom. id_flux_v, & ! surface flux of meridional mom. - id_temp_2m, & !mp586 for 10m winds and 2m temp - id_u_10m, & !mp586 for 10m winds and 2m temp - id_v_10m, & !mp586 for 10m winds and 2m temp - id_q_2m, & ! Add 2m specific humidity - id_rh_2m ! Add 2m relative humidity + id_temp_2m, & ! used for 10m winds and 2m temp + id_u_10m, & ! used for 10m winds and 2m temp + id_v_10m, & ! used for 10m winds and 2m temp + id_q_2m, & ! used for 2m specific humidity + id_rh_2m ! used for 2m relative humidity integer, allocatable, dimension(:,:) :: convflag ! indicates which qe convection subroutines are used real, allocatable, dimension(:,:) :: rad_lat, rad_lon -real, allocatable, dimension(:) :: pref, p_half_1d, ln_p_half_1d, p_full_1d,ln_p_full_1d !s pref is a reference pressure profile, which in 2006 MiMA is just the initial full pressure levels, and an extra level with the reference surface pressure. Others are only necessary to calculate pref. -real, allocatable, dimension(:,:) :: capeflag !s Added for Betts Miller scheme (rather than the simplified Betts Miller scheme). +real, allocatable, dimension(:) :: pref, p_half_1d, ln_p_half_1d, p_full_1d,ln_p_full_1d ! pref is a reference pressure profile, which in 2006 MiMA is just the initial full pressure levels, and an extra level with the reference surface pressure. Others are only necessary to calculate pref. +real, allocatable, dimension(:,:) :: capeflag ! Added for Betts Miller scheme (rather than the simplified Betts Miller scheme). type(surf_diff_type) :: Tri_surf ! used by gcm_vert_diff - -!s initialise constants ready to be used in rh_calc + +! initialise constants ready to be used in rh_calc real :: d622 = 0. real :: d378 = 0. - + logical :: used, doing_edt, doing_entrain, do_strat integer, dimension(4) :: axes integer :: is, ie, js, je, num_levels, nsphum, dt_integer @@ -303,14 +324,14 @@ subroutine idealized_moist_phys_init(Time, Time_step_in, nhum, rad_lon_2d, rad_l integer, intent(in) :: nhum real, intent(in), dimension(:,:) :: rad_lon_2d, rad_lat_2d, rad_lonb_2d, rad_latb_2d, t_surf_init -integer :: io, nml_unit, stdlog_unit, seconds, days, id, jd, kd -real, dimension (size(rad_lonb_2d,1)-1, size(rad_latb_2d,2)-1) :: sgsmtn !s added for damping_driver +integer :: io, ierr, nml_unit, stdlog_unit, seconds, days, id, jd, kd +real, dimension (size(rad_lonb_2d,1)-1, size(rad_latb_2d,2)-1) :: sgsmtn ! needed for damping_driver -!s added for land reading +! added for land reading integer, dimension(4) :: siz integer :: global_num_lon, global_num_lat character(len=12) :: ctmp1=' by ', ctmp2=' by ' -!s end added for land reading +! end added for land reading ! Added for RAS integer :: num_tracers=0,num_ras_tracers=0,n=0 @@ -327,24 +348,46 @@ subroutine idealized_moist_phys_init(Time, Time_step_in, nhum, rad_lon_2d, rad_l #ifdef INTERNAL_FILE_NML read (input_nml_file, nml=idealized_moist_phys_nml, iostat=io) + ierr = check_nml_error(io, 'idealized_moist_phys_nml') #else if ( file_exist('input.nml') ) then nml_unit = open_namelist_file() - read (nml_unit, idealized_moist_phys_nml, iostat=io) - call close_file(nml_unit) + ierr = 1 + do while (ierr /= 0) + read(nml_unit, idealized_moist_phys_nml, iostat=io, end=10) + ierr = check_nml_error(io, 'idealized_moist_phys_nml') + enddo +10 call close_file(nml_unit) endif #endif stdlog_unit = stdlog() write(stdlog_unit, idealized_moist_phys_nml) -!s initialise variables for rh_calc +! initialise variables for rh_calc d622 = rdgas/rvgas d378 = 1.-d622 -!s need to make sure that gray radiation and rrtm radiation are not both called. +if(do_cloud_simple .and. do_cloud_spookie) & + call error_mesg('cloud_simple','do_cloud_simple and do_cloud_spookie cannot both be .true.',FATAL) + +if(do_cloud_simple) then + call cloud_simple_init(get_axis_id(), Time) +end if +if(do_cloud_spookie) then + call cloud_spookie_init(get_axis_id(), Time) +end if + +! need to make sure that gray radiation and rrtm radiation are not both called. if(two_stream_gray .and. do_rrtm_radiation) & call error_mesg('physics_driver_init','do_grey_radiation and do_rrtm_radiation cannot both be .true.',FATAL) +if(two_stream_gray .and. (do_cloud_simple .or. do_cloud_spookie)) & + call error_mesg('idealized_moist_phys','Gray radiation is not configured to run with the cloud scheme at present.',FATAL) + +if(do_rrtm_radiation .and. (do_cloud_simple .or. do_cloud_spookie)) & + call error_mesg('idealized_moist_phys','RRTM is not configured to run with the cloud scheme at present.',FATAL) + + if(uppercase(trim(convection_scheme)) == 'NONE') then r_conv_scheme = NO_CONV lwet_convection = .false. @@ -358,7 +401,6 @@ subroutine idealized_moist_phys_init(Time, Time_step_in, nhum, rad_lon_2d, rad_l lwet_convection = .true. do_bm = .false. do_ras = .false. - else if(uppercase(trim(convection_scheme)) == 'FULL_BETTS_MILLER') then r_conv_scheme = FULL_BETTS_MILLER_CONV @@ -366,7 +408,6 @@ subroutine idealized_moist_phys_init(Time, Time_step_in, nhum, rad_lon_2d, rad_l do_bm = .true. lwet_convection = .false. do_ras = .false. - else if(uppercase(trim(convection_scheme)) == 'RAS') then r_conv_scheme = RAS_CONV @@ -380,7 +421,7 @@ subroutine idealized_moist_phys_init(Time, Time_step_in, nhum, rad_lon_2d, rad_l call error_mesg('idealized_moist_phys','Using dry convection scheme.', NOTE) lwet_convection = .false. do_bm = .false. - do_ras = .false. + do_ras = .false. else if(uppercase(trim(convection_scheme)) == 'UNSET') then call error_mesg('idealized_moist_phys','determining convection scheme from flags', NOTE) @@ -395,7 +436,7 @@ subroutine idealized_moist_phys_init(Time, Time_step_in, nhum, rad_lon_2d, rad_l if (do_ras) then r_conv_scheme = RAS_CONV call error_mesg('idealized_moist_phys','Using relaxed Arakawa Schubert convection scheme.', NOTE) - end if + end if else call error_mesg('idealized_moist_phys','"'//trim(convection_scheme)//'"'//' is not a valid convection scheme.'// & ' Choices are NONE, SIMPLE_BETTS, FULL_BETTS_MILLER, RAS, DRY', FATAL) @@ -403,12 +444,17 @@ subroutine idealized_moist_phys_init(Time, Time_step_in, nhum, rad_lon_2d, rad_l if(lwet_convection .and. do_bm) & call error_mesg('idealized_moist_phys','lwet_convection and do_bm cannot both be .true.',FATAL) - + if(lwet_convection .and. do_ras) & - call error_mesg('idealized_moist_phys','lwet_convection and do_ras cannot both be .true.',FATAL) + call error_mesg('idealized_moist_phys','lwet_convection and do_ras cannot both be .true.',FATAL) if(do_bm .and. do_ras) & - call error_mesg('idealized_moist_phys','do_bm and do_ras cannot both be .true.',FATAL) + call error_mesg('idealized_moist_phys','do_bm and do_ras cannot both be .true.',FATAL) + +if(do_lcl_diffusivity_depth .and. (.not. (lwet_convection .or. do_ras .or. do_bm))) & + call error_mesg('idealized_moist_phys','do_lcl_diffusivity_depth cannot be .true. if moist convection is not enabled',FATAL) + + nsphum = nhum Time_step = Time_step_in @@ -421,12 +467,12 @@ subroutine idealized_moist_phys_init(Time, Time_step_in, nhum, rad_lon_2d, rad_l allocate(rad_lat (is:ie, js:je)); rad_lat = rad_lat_2d allocate(rad_lon (is:ie, js:je)); rad_lon = rad_lon_2d -allocate (dt_bucket (is:ie, js:je)); dt_bucket = 0.0 ! RG Add bucket -allocate (filt (is:ie, js:je)); filt = 0.0 ! RG Add bucket -allocate(bucket_depth (is:ie, js:je, num_time_levels)); bucket_depth = init_bucket_depth ! RG Add bucket -allocate(depth_change_lh(is:ie, js:je)) ! RG Add bucket -allocate(depth_change_cond(is:ie, js:je)) ! RG Add bucket -allocate(depth_change_conv(is:ie, js:je)) ! RG Add bucket +allocate(dt_bucket (is:ie, js:je)); dt_bucket = 0.0 +allocate(filt (is:ie, js:je)); filt = 0.0 +allocate(bucket_depth (is:ie, js:je, num_time_levels)); bucket_depth = init_bucket_depth +allocate(depth_change_lh(is:ie, js:je)) +allocate(depth_change_cond(is:ie, js:je)) +allocate(depth_change_conv(is:ie, js:je)) allocate(z_surf (is:ie, js:je)) allocate(t_surf (is:ie, js:je)) allocate(q_surf (is:ie, js:je)); q_surf = 0.0 @@ -457,14 +503,14 @@ subroutine idealized_moist_phys_init(Time, Time_step_in, nhum, rad_lon_2d, rad_l allocate(dedq_atm (is:ie, js:je)) allocate(dtaudv_atm (is:ie, js:je)) allocate(dtaudu_atm (is:ie, js:je)) -allocate(ex_del_m (is:ie, js:je)) !mp586 added for 10m wind and 2m temp -allocate(ex_del_h (is:ie, js:je)) !mp586 added for 10m wind and 2m temp -allocate(ex_del_q (is:ie, js:je)) !mp586 added for 10m wind and 2m temp -allocate(temp_2m (is:ie, js:je)) !mp586 added for 10m wind and 2m temp -allocate(u_10m (is:ie, js:je)) !mp586 added for 10m wind and 2m temp -allocate(v_10m (is:ie, js:je)) !mp586 added for 10m wind and 2m temp -allocate(q_2m (is:ie, js:je)) ! Add 2m specific humidity -allocate(rh_2m (is:ie, js:je)) ! Add 2m relative humidity +allocate(ex_del_m (is:ie, js:je)) +allocate(ex_del_h (is:ie, js:je)) +allocate(ex_del_q (is:ie, js:je)) +allocate(temp_2m (is:ie, js:je)) +allocate(u_10m (is:ie, js:je)) +allocate(v_10m (is:ie, js:je)) +allocate(q_2m (is:ie, js:je)) +allocate(rh_2m (is:ie, js:je)) allocate(land (is:ie, js:je)); land = .false. allocate(land_ones (is:ie, js:je)); land_ones = 0.0 allocate(avail (is:ie, js:je)); avail = .true. @@ -473,7 +519,7 @@ subroutine idealized_moist_phys_init(Time, Time_step_in, nhum, rad_lon_2d, rad_l allocate(diff_t (is:ie, js:je, num_levels)) allocate(diff_m (is:ie, js:je, num_levels)) allocate(diss_heat (is:ie, js:je, num_levels)) -allocate(diss_heat_ray (is:ie, js:je, num_levels)) !s added for rayleigh_bottom_drag, used when gp_surface=.True. +allocate(diss_heat_ray (is:ie, js:je, num_levels)) ! added for rayleigh_bottom_drag, used when gp_surface=.True. allocate(tdtlw (is:ie, js:je, num_levels)); tdtlw = 0.0 allocate(non_diff_dt_ug (is:ie, js:je, num_levels)) @@ -489,7 +535,8 @@ subroutine idealized_moist_phys_init(Time, Time_step_in, nhum, rad_lon_2d, rad_l allocate(cond_dt_qg (is:ie, js:je, num_levels)) allocate(coldT (is:ie, js:je)); coldT = .false. -allocate(klzbs (is:ie, js:je)) +allocate(klzbs (is:ie, js:je)); klzbs = 0 +allocate(klcls (is:ie, js:je)); klcls = 0 allocate(cape (is:ie, js:je)) allocate(cin (is:ie, js:je)) allocate(invtau_q_relaxation (is:ie, js:je)) @@ -497,17 +544,20 @@ subroutine idealized_moist_phys_init(Time, Time_step_in, nhum, rad_lon_2d, rad_l allocate(rain (is:ie, js:je)); rain = 0.0 allocate(snow (is:ie, js:je)); snow = 0.0 allocate(precip (is:ie, js:je)); precip = 0.0 +allocate(convective_rain (is:ie, js:je)); convective_rain = 0.0 allocate(convflag (is:ie, js:je)) allocate(convect (is:ie, js:je)); convect = .false. + + allocate(t_ref (is:ie, js:je, num_levels)); t_ref = 0.0 allocate(q_ref (is:ie, js:je, num_levels)); q_ref = 0.0 -allocate (albedo (is:ie, js:je)) !s allocate for albedo, to be set in mixed_layer_init. -allocate(coszen (is:ie, js:je)) !s allocate coszen to be set in run_rrtmg -allocate(pbltop (is:ie, js:je)) !s allocate coszen to be set in run_rrtmg +allocate (albedo (is:ie, js:je)) ! allocate for albedo, to be set in mixed_layer_init. +allocate(coszen (is:ie, js:je)) ! allocate coszen to be set in run_rrtmg +allocate(pbltop (is:ie, js:je)) ! allocate coszen to be set in run_rrtmg -allocate(pref(num_levels+1)) !s reference pressure profile, as in spectral_physics.f90 in FMS 2006 and original MiMA. +allocate(pref(num_levels+1)) ! reference pressure profile, as in spectral_physics.f90 in FMS 2006 and original MiMA. allocate(p_half_1d(num_levels+1), ln_p_half_1d(num_levels+1)) allocate(p_full_1d(num_levels ), ln_p_full_1d(num_levels )) allocate(capeflag (is:ie, js:je)) @@ -515,62 +565,55 @@ subroutine idealized_moist_phys_init(Time, Time_step_in, nhum, rad_lon_2d, rad_l call get_surf_geopotential(z_surf) z_surf = z_surf/grav -!s initialise the land area +! initialise the land area if(trim(land_option) .eq. 'input')then -!s read in land nc file -!s adapted from spectral_init_cond.F90 - - if(file_exist(trim(land_file_name))) then - call mpp_get_global_domain(grid_domain, xsize=global_num_lon, ysize=global_num_lat) - call field_size(trim(land_file_name), trim(land_field_name), siz) - if ( siz(1) == global_num_lon .or. siz(2) == global_num_lat ) then - call read_data(trim(land_file_name), trim(land_field_name), land_ones, grid_domain) - !s write something to screen to let the user know what's happening. - else - write(ctmp1(1: 4),'(i4)') siz(1) - write(ctmp1(9:12),'(i4)') siz(2) - write(ctmp2(1: 4),'(i4)') global_num_lon - write(ctmp2(9:12),'(i4)') global_num_lat - call error_mesg ('idealized_moist_phys','Land file contains data on a '// & - ctmp1//' grid, but atmos model grid is '//ctmp2, FATAL) - endif - else - call error_mesg('idealized_moist_phys','land_option="'//trim(land_option)//'"'// & - ' but '//trim(land_file_name)//' does not exist', FATAL) - endif - - !s convert data in land nc file to land logical array - where(land_ones > 0.) land = .true. +! read in land nc file +! adapted from spectral_init_cond.F90 + + if(file_exist(trim(land_file_name))) then + call mpp_get_global_domain(grid_domain, xsize=global_num_lon, ysize=global_num_lat) + call field_size(trim(land_file_name), trim(land_field_name), siz) + if ( siz(1) == global_num_lon .or. siz(2) == global_num_lat ) then + call read_data(trim(land_file_name), trim(land_field_name), land_ones, grid_domain) + ! write something to screen to let the user know what's happening. + else + write(ctmp1(1: 4),'(i4)') siz(1) + write(ctmp1(9:12),'(i4)') siz(2) + write(ctmp2(1: 4),'(i4)') global_num_lon + write(ctmp2(9:12),'(i4)') global_num_lat + call error_mesg ('idealized_moist_phys','Land file contains data on a '// & + ctmp1//' grid, but atmos model grid is '//ctmp2, FATAL) + endif + else + call error_mesg('idealized_moist_phys','land_option="'//trim(land_option)//'"'// & + ' but '//trim(land_file_name)//' does not exist', FATAL) + endif + + ! convert data in land nc file to land logical array + where(land_ones > 0.) land = .true. elseif(trim(land_option) .eq. 'zsurf')then - !s wherever zsurf is greater than some threshold height then make land = .true. - where ( z_surf > 10. ) land = .true. + ! wherever zsurf is greater than some threshold height then make land = .true. + where ( z_surf > 10. ) land = .true. endif - -!s Add option to alter surface roughness length over land - +!option to alter surface roughness length over land if(trim(land_option) .eq. 'input') then - where(land) - rough_mom = land_roughness_prefactor * rough_mom - rough_heat = land_roughness_prefactor * rough_heat - rough_moist = land_roughness_prefactor * rough_moist - end where + where(land) + rough_mom = land_roughness_prefactor * rough_mom + rough_heat = land_roughness_prefactor * rough_heat + rough_moist = land_roughness_prefactor * rough_moist + end where endif -!RG Add bucket - initialise bucket depth if(bucket) then where(land) bucket_depth(:,:,1) = init_bucket_depth_land bucket_depth(:,:,2) = init_bucket_depth_land end where endif -!RG end Add bucket - -!s end option to alter surface roughness length over land - if (gp_surface) then call rayleigh_bottom_drag_init(get_axis_id(), Time) @@ -583,9 +626,12 @@ subroutine idealized_moist_phys_init(Time, Time_step_in, nhum, rad_lon_2d, rad_l ! initialize damping_driver_mod. if(do_damping) then call pressure_variables(p_half_1d,ln_p_half_1d,pref(1:num_levels),ln_p_full_1d,PSTD_MKS) - pref(num_levels+1) = PSTD_MKS - call damping_driver_init (rad_lonb_2d(:,1),rad_latb_2d(1,:), pref(:), get_axis_id(), Time, & !s note that in the original this is pref(:,1), which is the full model pressure levels and the surface pressure at the bottom. There is pref(:2) in this version with 81060 as surface pressure?? - sgsmtn) + pref(num_levels+1) = PSTD_MKS + call damping_driver_init(rad_lonb_2d(:,1),rad_latb_2d(1,:), pref(:), get_axis_id(), Time, & + sgsmtn) + !note that in the original this is pref(:,1), which is the full model pressure levels and + !the surface pressure at the bottom. There is pref(:2) in this version with 81060 as + !surface pressure?? endif @@ -596,8 +642,8 @@ subroutine idealized_moist_phys_init(Time, Time_step_in, nhum, rad_lon_2d, rad_l ! to quickly enter the atmosphere avoiding problems with the convection scheme t_surf = t_surf_init + 1.0 - call mixed_layer_init(is, ie, js, je, num_levels, t_surf, bucket_depth, get_axis_id(), Time, albedo, rad_lonb_2d(:,:), rad_latb_2d(:,:), land, bucket) ! t_surf is intent(inout) !s albedo distribution set here. - + call mixed_layer_init(is, ie, js, je, num_levels, t_surf, bucket_depth, get_axis_id(), Time, albedo, rad_lonb_2d(:,:), rad_latb_2d(:,:), land, bucket) ! t_surf is intent(inout) ! albedo distribution set here. + elseif(gp_surface) then albedo=0.0 call error_mesg('idealized_moist_phys','Because gp_surface=.True., setting albedo=0.0', NOTE) @@ -610,7 +656,7 @@ subroutine idealized_moist_phys_init(Time, Time_step_in, nhum, rad_lon_2d, rad_l ! need to call vert_diff_init even if using gcm_vert_diff (rather than ! gcm_vert_diff_down) because the variable sphum is not initialized ! otherwise in the vert_diff module - call vert_diff_init (Tri_surf, ie-is+1, je-js+1, num_levels, .true., do_virtual) !s do_conserve_energy is hard-coded in. + call vert_diff_init (Tri_surf, ie-is+1, je-js+1, num_levels, .true., do_virtual) ! do_conserve_energy is hard-coded in. end if call lscale_cond_init() @@ -635,33 +681,27 @@ subroutine idealized_moist_phys_init(Time, Time_step_in, nhum, rad_lon_2d, rad_l axes(1:2), Time, 'Meridional momentum flux', 'Pa') if(bucket) then - id_bucket_depth = register_diag_field(mod_name, 'bucket_depth', & ! RG Add bucket + id_bucket_depth = register_diag_field(mod_name, 'bucket_depth', & axes(1:2), Time, 'Depth of surface reservoir', 'm') - id_bucket_depth_conv = register_diag_field(mod_name, 'bucket_depth_conv', & ! RG Add bucket + id_bucket_depth_conv = register_diag_field(mod_name, 'bucket_depth_conv', & axes(1:2), Time, 'Tendency of bucket depth induced by Convection', 'm/s') - id_bucket_depth_cond = register_diag_field(mod_name, 'bucket_depth_cond', & ! RG Add bucket + id_bucket_depth_cond = register_diag_field(mod_name, 'bucket_depth_cond', & axes(1:2), Time, 'Tendency of bucket depth induced by Condensation', 'm/s') - id_bucket_depth_lh = register_diag_field(mod_name, 'bucket_depth_lh', & ! RG Add bucket + id_bucket_depth_lh = register_diag_field(mod_name, 'bucket_depth_lh', & axes(1:2), Time, 'Tendency of bucket depth induced by LH', 'm/s') endif -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!!!!!!! added by mp586 for 10m winds and 2m temperature add mo_profile()!!!!!!!! - -id_temp_2m = register_diag_field(mod_name, 'temp_2m', & !mp586 add 2m temp +id_temp_2m = register_diag_field(mod_name, 'temp_2m', & axes(1:2), Time, 'Air temperature 2m above surface', 'K') -id_u_10m = register_diag_field(mod_name, 'u_10m', & !mp586 add 10m wind (u) +id_u_10m = register_diag_field(mod_name, 'u_10m', & axes(1:2), Time, 'Zonal wind 10m above surface', 'm/s') -id_v_10m = register_diag_field(mod_name, 'v_10m', & !mp586 add 10m wind (v) +id_v_10m = register_diag_field(mod_name, 'v_10m', & axes(1:2), Time, 'Meridional wind 10m above surface', 'm/s') -!!!!!!!!!!!! end of mp586 additions !!!!!!!!!!!!!!!!!!!!!!! -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - -id_q_2m = register_diag_field(mod_name, 'sphum_2m', & - axes(1:2), Time, 'Specific humidity 2m above surface', 'kg/kg') !Add 2m specific humidity +id_q_2m = register_diag_field(mod_name, 'sphum_2m', & + axes(1:2), Time, 'Specific humidity 2m above surface', 'kg/kg') id_rh_2m = register_diag_field(mod_name, 'rh_2m', & - axes(1:2), Time, 'Relative humidity 2m above surface', 'percent') !Add 2m relative humidity + axes(1:2), Time, 'Relative humidity 2m above surface', 'percent') select case(r_conv_scheme) @@ -679,7 +719,7 @@ subroutine idealized_moist_phys_init(Time, Time_step_in, nhum, rad_lon_2d, rad_l !run without startiform cloud scheme !--------------------------------------------------------------------- - ! retrieve the number of registered tracers in order to determine + ! retrieve the number of registered tracers in order to determine ! which tracers are to be convectively transported. !--------------------------------------------------------------------- @@ -691,7 +731,7 @@ subroutine idealized_moist_phys_init(Time, Time_step_in, nhum, rad_lon_2d, rad_l do_strat = .false. !Commented code not used such that tracers are not advected by RAS. Could implement in future. - + ! do n=1, num_tracers ! if (query_method ('convection', MODEL_ATMOS, n, scheme)) then ! num_ras_tracers = num_ras_tracers + 1 @@ -707,13 +747,13 @@ subroutine idealized_moist_phys_init(Time, Time_step_in, nhum, rad_lon_2d, rad_l !---------------------------------------------------------------------- ! for each tracer, determine if it is to be transported by convect- - ! ion, and the convection schemes that are to transport it. set a + ! ion, and the convection schemes that are to transport it. set a ! logical flag to .true. for each tracer that is to be transported by ! each scheme and increment the count of tracers to be transported ! by that scheme. !---------------------------------------------------------------------- - call ras_init (do_strat, axes,Time,tracers_in_ras) + call ras_init (do_strat, axes,Time,tracers_in_ras) end select @@ -727,7 +767,6 @@ subroutine idealized_moist_phys_init(Time, Time_step_in, nhum, rad_lon_2d, rad_l axes(1:2), Time, 'Rain from convection','kg/m/m/s') !endif - if(two_stream_gray) call two_stream_gray_rad_init(is, ie, js, je, num_levels, get_axis_id(), Time, rad_lonb_2d, rad_latb_2d, dt_real) #ifdef RRTM_NO_COMPILE @@ -736,7 +775,7 @@ subroutine idealized_moist_phys_init(Time, Time_step_in, nhum, rad_lon_2d, rad_l endif #else if(do_rrtm_radiation) then - id=ie-is+1 !s Taking dimensions from equivalend calls in vert_turb_driver_init + id=ie-is+1 ! Taking dimensions from equivalend calls in vert_turb_driver_init jd=je-js+1 kd=num_levels call rrtmg_lw_ini(cp_air) @@ -753,7 +792,7 @@ subroutine idealized_moist_phys_init(Time, Time_step_in, nhum, rad_lon_2d, rad_l endif #else if (do_socrates_radiation) then - call socrates_init(is, ie, js, je, num_levels, axes, Time, rad_lat, rad_lonb_2d, rad_latb_2d, Time_step_in) + call socrates_init(is, ie, js, je, num_levels, axes, Time, rad_lat, rad_lonb_2d, rad_latb_2d, Time_step_in, do_cloud_simple, do_cloud_spookie) endif #endif @@ -767,21 +806,22 @@ subroutine idealized_moist_phys_init(Time, Time_step_in, nhum, rad_lon_2d, rad_l id_diff_dt_vg = register_diag_field(mod_name, 'dt_vg_diffusion', & axes(1:3), Time, 'meridional wind tendency from diffusion','m/s^2') id_diff_dt_tg = register_diag_field(mod_name, 'dt_tg_diffusion', & - axes(1:3), Time, 'temperature diffusion tendency','T/s') + axes(1:3), Time, 'temperature diffusion tendency','K/s') id_diff_dt_qg = register_diag_field(mod_name, 'dt_qg_diffusion', & - axes(1:3), Time, 'moisture diffusion tendency','T/s') + axes(1:3), Time, 'moisture diffusion tendency','kg/kg/s') endif - id_rh = register_diag_field ( mod_name, 'rh', & - axes(1:3), Time, 'relative humidity', 'percent') + id_rh = register_diag_field ( mod_name, 'rh', & + axes(1:3), Time, 'relative humidity', 'percent') end subroutine idealized_moist_phys_init !================================================================================================================================= -subroutine idealized_moist_phys(Time, p_half, p_full, z_half, z_full, ug, vg, tg, grid_tracers, & +subroutine idealized_moist_phys(Time, p_half, p_full, z_half, z_full, ug, vg, psg, wg_full, tg, grid_tracers, & previous, current, dt_ug, dt_vg, dt_tg, dt_tracers, mask, kbot) type(time_type), intent(in) :: Time real, dimension(:,:,:,:), intent(in) :: p_half, p_full, z_half, z_full, ug, vg, tg +real, dimension(:,:,:), intent(in) :: psg, wg_full real, dimension(:,:,:,:,:), intent(in) :: grid_tracers integer, intent(in) :: previous, current real, dimension(:,:,:), intent(inout) :: dt_ug, dt_vg, dt_tg @@ -790,6 +830,9 @@ subroutine idealized_moist_phys(Time, p_half, p_full, z_half, z_full, ug, vg, tg real :: delta_t real, dimension(size(ug,1), size(ug,2), size(ug,3)) :: tg_tmp, qg_tmp, RH,tg_interp, mc, dt_ug_conv, dt_vg_conv +! Simple cloud scheme variabilies to pass to radiation +real, dimension(size(ug,1), size(ug,2), size(ug,3)) :: cf_rad, reff_rad, qcl_rad, cca_rad + real, intent(in) , dimension(:,:,:), optional :: mask integer, intent(in) , dimension(:,:), optional :: kbot @@ -803,11 +846,14 @@ subroutine idealized_moist_phys(Time, p_half, p_full, z_half, z_full, ug, vg, tg endif if (bucket) then - dt_bucket = 0.0 ! RG Add bucket - filt = 0.0 ! RG Add bucket + dt_bucket = 0.0 + filt = 0.0 endif -rain = 0.0; snow = 0.0; precip = 0.0 + +rain = 0.0; snow = 0.0; precip = 0.0; klcls = 0 +convective_rain = 0.0 + select case(r_conv_scheme) @@ -821,7 +867,8 @@ subroutine idealized_moist_phys(Time, p_half, p_full, z_half, z_full, ug, vg, tg q_ref, convflag, & klzbs, cape, & cin, invtau_q_relaxation, & - invtau_t_relaxation, t_ref) + invtau_t_relaxation, t_ref, & + klcls) tg_tmp = conv_dt_tg + tg(:,:,:,previous) qg_tmp = conv_dt_qg + grid_tracers(:,:,:,previous,nsphum) @@ -829,7 +876,7 @@ subroutine idealized_moist_phys(Time, p_half, p_full, z_half, z_full, ug, vg, tg conv_dt_tg = conv_dt_tg/delta_t conv_dt_qg = conv_dt_qg/delta_t - depth_change_conv = rain/dens_h2o ! RG Add bucket + depth_change_conv = rain/dens_h2o rain = rain/delta_t precip = rain @@ -850,7 +897,7 @@ subroutine idealized_moist_phys(Time, p_half, p_full, z_half, z_full, ug, vg, tg klzbs, cape, & cin, t_ref, & invtau_t_relaxation, invtau_q_relaxation, & - capeflag) + capeflag, klcls) tg_tmp = conv_dt_tg + tg(:,:,:,previous) qg_tmp = conv_dt_qg + grid_tracers(:,:,:,previous,nsphum) @@ -858,7 +905,7 @@ subroutine idealized_moist_phys(Time, p_half, p_full, z_half, z_full, ug, vg, tg conv_dt_tg = conv_dt_tg/delta_t conv_dt_qg = conv_dt_qg/delta_t - depth_change_conv = rain/dens_h2o ! RG Add bucket + depth_change_conv = rain/dens_h2o rain = rain/delta_t precip = rain @@ -871,7 +918,7 @@ subroutine idealized_moist_phys(Time, p_half, p_full, z_half, z_full, ug, vg, tg case(DRY_CONV) call dry_convection(Time, tg(:, :, :, previous), & p_full(:,:,:,previous), p_half(:,:,:,previous), & - conv_dt_tg, cape, cin) + conv_dt_tg, cape, cin, klzbs, klcls) tg_tmp = conv_dt_tg*delta_t + tg(:,:,:,previous) qg_tmp = grid_tracers(:,:,:,previous,nsphum) @@ -882,19 +929,20 @@ subroutine idealized_moist_phys(Time, p_half, p_full, z_half, z_full, ug, vg, tg case(RAS_CONV) - call ras (is, js, Time, & + call ras (is, js, Time, & tg(:,:,:,previous), grid_tracers(:,:,:,previous,nsphum), & ug(:,:,:,previous), vg(:,:,:,previous), p_full(:,:,:,previous), & p_half(:,:,:,previous), z_half(:,:,:,previous), coldT, delta_t, & conv_dt_tg, conv_dt_qg, dt_ug_conv, dt_vg_conv, & - rain, snow, do_strat, & - !OPTIONAL + rain, snow, do_strat, & + klzbs, klcls, & + !OPTIONAL mask, kbot, & !OPTIONAL OUT mc, tracer(:,:,:), tracer(:,:,:), & tracer(:,:,:), tracertnd(:,:,:), & tracertnd(:,:,:), tracertnd(:,:,:)) - + !update tendencies - dT and dq are done after cases tg_tmp = tg(:,:,:,previous) + conv_dt_tg @@ -910,6 +958,7 @@ subroutine idealized_moist_phys(Time, p_half, p_full, z_half, z_full, ug, vg, tg case(NO_CONV) + conv_dt_tg = 0.0 tg_tmp = tg(:,:,:,previous) qg_tmp = grid_tracers(:,:,:,previous,nsphum) @@ -918,11 +967,11 @@ subroutine idealized_moist_phys(Time, p_half, p_full, z_half, z_full, ug, vg, tg end select - ! Add the T and q tendencies due to convection to the timestep dt_tg = dt_tg + conv_dt_tg dt_tracers(:,:,:,nsphum) = dt_tracers(:,:,:,nsphum) + conv_dt_qg +convective_rain = precip ! Perform large scale convection if (r_conv_scheme .ne. DRY_CONV) then @@ -937,7 +986,7 @@ subroutine idealized_moist_phys(Time, p_half, p_full, z_half, z_full, ug, vg, tg cond_dt_tg = cond_dt_tg/delta_t cond_dt_qg = cond_dt_qg/delta_t - depth_change_cond = rain/dens_h2o ! RG Add bucket + depth_change_cond = rain/dens_h2o rain = rain/delta_t snow = snow/delta_t precip = precip + rain + snow @@ -952,6 +1001,51 @@ subroutine idealized_moist_phys(Time, p_half, p_full, z_half, z_full, ug, vg, tg endif +! Call the simple cloud scheme in line with SPOOKIE-2 requirements +! Using start of time step variables +! using specific humidity NOT mixing ratios + +! initialise outs to zero + + !Set to zero regardless of if clouds are used in radiation code + cf_rad = 0. + reff_rad = 0. + qcl_rad = 1e-8 + cca_rad = 0. + +if(do_cloud_simple) then + call cloud_simple(p_half(:,:,:,current), & + p_full(:,:,:,current), & + Time, & + tg(:,:,:,previous), & + grid_tracers(:,:,:,previous,nsphum), & + z_full(:,:,:,current), & + wg_full(:,:,:), & + psg(:,:,current), & + temp_2m(:,:), & + q_2m(:,:), & + rh_2m(:,:), & + klcls(:,:), & + .not. land(:,:), & ! ocean mask, True is for ocean + ! ----- outs ----- + cf_rad(:,:,:), & + reff_rad(:,:,:), & + qcl_rad(:,:,:) ) +elseif(do_cloud_spookie) then + cf_rad(:,:,:) = 0. + reff_rad(:,:,:) = 0. + qcl_rad(:,:,:) = 0. + cca_rad(:,:,:) = 0. + + call cloud_spookie(p_half(:,:,:,current), & + p_full(:,:,:,current), & + Time, & + tg(:,:,:,previous), & + grid_tracers(:,:,:,previous,nsphum), & + ! inouts - + cf_rad(:,:,:), cca_rad(:,:,:), & + reff_rad(:,:,:), qcl_rad(:,:,:) ) +endif ! Begin the radiation calculation by computing downward fluxes. ! This part of the calculation does not depend on the surface temperature. @@ -990,71 +1084,71 @@ subroutine idealized_moist_phys(Time, p_half, p_full, z_half, z_full, ug, vg, tg p_half(:,:,num_levels+1,current), & t_surf(:,:), & t_surf(:,:), & - q_surf(:,:), & ! is intent(inout) - bucket, & ! RG Add bucket - bucket_depth(:,:,current), & ! RG Add bucket - max_bucket_depth_land, & ! RG Add bucket - depth_change_lh(:,:), & ! RG Add bucket - depth_change_conv(:,:), & ! RG Add bucket - depth_change_cond(:,:), & ! RG Add bucket + q_surf(:,:), & + bucket, & + bucket_depth(:,:,current), & + max_bucket_depth_land, & + depth_change_lh(:,:), & + depth_change_conv(:,:), & + depth_change_cond(:,:), & u_surf(:,:), & v_surf(:,:), & rough_mom(:,:), & rough_heat(:,:), & rough_moist(:,:), & - rough_mom(:,:), & ! using rough_mom in place of rough_scale -- pjp + rough_mom(:,:), & gust(:,:), & - flux_t(:,:), & ! is intent(out) - flux_q(:,:), & ! is intent(out) - flux_r(:,:), & ! is intent(out) - flux_u(:,:), & ! is intent(out) - flux_v(:,:), & ! is intent(out) - drag_m(:,:), & ! is intent(out) - drag_t(:,:), & ! is intent(out) - drag_q(:,:), & ! is intent(out) - w_atm(:,:), & ! is intent(out) - ustar(:,:), & ! is intent(out) - bstar(:,:), & ! is intent(out) - qstar(:,:), & ! is intent(out) - dhdt_surf(:,:), & ! is intent(out) - dedt_surf(:,:), & ! is intent(out) - dedq_surf(:,:), & ! is intent(out) - drdt_surf(:,:), & ! is intent(out) - dhdt_atm(:,:), & ! is intent(out) - dedq_atm(:,:), & ! is intent(out) - dtaudu_atm(:,:), & ! is intent(out) - dtaudv_atm(:,:), & ! is intent(out) - ex_del_m(:,:), & ! mp586 for 10m winds and 2m temp - ex_del_h(:,:), & ! mp586 for 10m winds and 2m temp - ex_del_q(:,:), & ! mp586 for 10m winds and 2m temp - temp_2m(:,:), & ! mp586 for 10m winds and 2m temp - u_10m(:,:), & ! mp586 for 10m winds and 2m temp - v_10m(:,:), & ! mp586 for 10m winds and 2m temp - q_2m(:,:), & ! Add 2m specific humidity - rh_2m(:,:), & ! Add 2m relative humidity - delta_t, & + flux_t(:,:), & + flux_q(:,:), & + flux_r(:,:), & + flux_u(:,:), & + flux_v(:,:), & + drag_m(:,:), & + drag_t(:,:), & + drag_q(:,:), & + w_atm(:,:), & + ustar(:,:), & + bstar(:,:), & + qstar(:,:), & + dhdt_surf(:,:), & + dedt_surf(:,:), & + dedq_surf(:,:), & + drdt_surf(:,:), & + dhdt_atm(:,:), & + dedq_atm(:,:), & + dtaudu_atm(:,:), & + dtaudv_atm(:,:), & + ex_del_m(:,:), & + ex_del_h(:,:), & + ex_del_q(:,:), & + temp_2m(:,:), & + u_10m(:,:), & + v_10m(:,:), & + q_2m(:,:), & + rh_2m(:,:), & + delta_t, & land(:,:), & .not.land(:,:), & avail(:,:) ) if(id_flux_u > 0) used = send_data(id_flux_u, flux_u, Time) if(id_flux_v > 0) used = send_data(id_flux_v, flux_v, Time) + if(id_temp_2m > 0) used = send_data(id_temp_2m, temp_2m, Time) + if(id_u_10m > 0) used = send_data(id_u_10m, u_10m, Time) + if(id_v_10m > 0) used = send_data(id_v_10m, v_10m, Time) + if(id_q_2m > 0) used = send_data(id_q_2m, q_2m, Time) + if(id_rh_2m > 0) used = send_data(id_rh_2m, rh_2m*1e2, Time) - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !!!!!!! added by mp586 for 10m winds and 2m temperature add mo_profile()!!!!!!!! - - if(id_temp_2m > 0) used = send_data(id_temp_2m, temp_2m, Time) ! mp586 add 2m temp - if(id_u_10m > 0) used = send_data(id_u_10m, u_10m, Time) ! mp586 add 10m wind (u) - if(id_v_10m > 0) used = send_data(id_v_10m, v_10m, Time) ! mp586 add 10m wind (v) - +endif - !!!!!!!!!!!! end of mp586 additions !!!!!!!!!!!!!!!!!!!!!!! - !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +! 10m winds and 2m temperature add mo_profile() - if(id_q_2m > 0) used = send_data(id_q_2m, q_2m, Time) ! Add 2m specific humidity - if(id_rh_2m > 0) used = send_data(id_rh_2m, rh_2m*1e2, Time) ! Add 2m relative humidity +if(id_temp_2m > 0) used = send_data(id_temp_2m, temp_2m, Time) ! 2m temp +if(id_u_10m > 0) used = send_data(id_u_10m, u_10m, Time) ! 10m wind (u) +if(id_v_10m > 0) used = send_data(id_v_10m, v_10m, Time) ! 10m wind (v) -endif +if(id_q_2m > 0) used = send_data(id_q_2m, q_2m, Time) ! Add 2m humidity +if(id_rh_2m > 0) used = send_data(id_rh_2m, rh_2m*1e2, Time) ! Add 2m humidity ! Now complete the radiation calculation by computing the upward and net fluxes. @@ -1074,9 +1168,12 @@ subroutine idealized_moist_phys(Time, p_half, p_full, z_half, z_full, ug, vg, tg #else if(do_rrtm_radiation) then !need t at half grid - tg_interp=tg(:,:,:,previous) + tg_interp=tg(:,:,:,previous) call interp_temp(z_full(:,:,:,current),z_half(:,:,:,current),tg_interp, Time) - call run_rrtmg(is,js,Time,rad_lat(:,:),rad_lon(:,:),p_full(:,:,:,current),p_half(:,:,:,current),albedo,grid_tracers(:,:,:,previous,nsphum),tg_interp,t_surf(:,:),dt_tg(:,:,:),coszen,net_surf_sw_down(:,:),surf_lw_down(:,:)) + call run_rrtmg(is,js,Time,rad_lat(:,:),rad_lon(:,:),p_full(:,:,:,current),p_half(:,:,:,current), & + albedo,grid_tracers(:,:,:,previous,nsphum),tg_interp,t_surf(:,:),dt_tg(:,:,:), & + coszen,net_surf_sw_down(:,:),surf_lw_down(:,:))!, cf_rad(:,:,:), reff_rad(:,:,:), & + !do_cloud_simple ) endif #endif @@ -1086,33 +1183,41 @@ subroutine idealized_moist_phys(Time, p_half, p_full, z_half, z_full, ug, vg, tg endif #else if (do_socrates_radiation) then - ! Socrates interface - - call run_socrates(Time, Time+Time_step, rad_lat, rad_lon, tg(:,:,:,previous), grid_tracers(:,:,:,previous,nsphum), t_surf(:,:), p_full(:,:,:,current), & - p_half(:,:,:,current),z_full(:,:,:,current),z_half(:,:,:,current), albedo, dt_tg(:,:,:), net_surf_sw_down(:,:), surf_lw_down(:,:), delta_t) - + ! Socrates interface + if((do_cloud_simple) .or. (do_cloud_spookie)) then + ! Simple cloud scheme outputs radii in microns, but Socrates expects + ! it in metres so convert it. + reff_rad = 1.e-6 * reff_rad + endif + + call run_socrates(Time, Time+Time_step, rad_lat, rad_lon, & + tg(:,:,:,previous), grid_tracers(:,:,:,previous,nsphum), & + t_surf(:,:), p_full(:,:,:,current), & + p_half(:,:,:,current), z_full(:,:,:,current), & + z_half(:,:,:,current), albedo, dt_tg(:,:,:), & + net_surf_sw_down(:,:), surf_lw_down(:,:), delta_t, & + do_cloud_simple, do_cloud_spookie, cf_rad(:,:,:), & + reff_rad(:,:,:), qcl_rad(:,:,:) ) endif #endif if(gp_surface) then - call gp_surface_flux (dt_tg(:,:,:), p_half(:,:,:,current), num_levels) - + call gp_surface_flux (dt_tg(:,:,:), p_half(:,:,:,current), num_levels) + call compute_rayleigh_bottom_drag( 1, ie-is+1, & 1, je-js+1, & Time, delta_t, & - rad_lat(:,:), dt_ug(:,:,: ), & + rad_lat(:,:), dt_ug(:,:,: ), & dt_vg(:,:,: ), & ug(:,:,:,previous), vg(:,:,:,previous), & p_half(:,:,:,previous), p_full(:,:,:,previous), & dt_tg, diss_heat_ray ) - if(id_diss_heat_ray > 0) used = send_data(id_diss_heat_ray, diss_heat_ray, Time) + if(id_diss_heat_ray > 0) used = send_data(id_diss_heat_ray, diss_heat_ray, Time) endif - - !---------------------------------------------------------------------- ! Copied from MiMA physics_driver.f90 ! call damping_driver to calculate the various model dampings that @@ -1120,7 +1225,7 @@ subroutine idealized_moist_phys(Time, p_half, p_full, z_half, z_full, ug, vg, tg !---------------------------------------------------------------------- z_pbl(:,:) = pbltop(is:ie,js:je) if(do_damping) then - call damping_driver (is, js, rad_lat, Time+Time_step, delta_t, & + call damping_driver (is, js, rad_lat, Time+Time_step, delta_t, & p_full(:,:,:,current), p_half(:,:,:,current), & z_full(:,:,:,current), z_half(:,:,:,current), & ug(:,:,:,previous), vg(:,:,:,previous), & @@ -1128,12 +1233,10 @@ subroutine idealized_moist_phys(Time, p_half, p_full, z_half, z_full, ug, vg, tg grid_tracers(:,:,:,previous,:), & dt_ug(:,:,:), dt_vg(:,:,:), dt_tg(:,:,:), & dt_tracers(:,:,:,nsphum), dt_tracers(:,:,:,:), & - z_pbl) !s have taken the names of arrays etc from vert_turb_driver below. Watch ntp from 2006 call to this routine? + z_pbl) ! have taken the names of arrays etc from vert_turb_driver below. Watch ntp from 2006 call to this routine? endif - - if(turb) then call vert_turb_driver( 1, 1, & @@ -1152,11 +1255,12 @@ subroutine idealized_moist_phys(Time, p_half, p_full, z_half, z_full, ug, vg, tg grid_tracers(:,:,:,previous,nsphum), grid_tracers(:,:,:,previous,:), & dt_ug(:,:,:), dt_vg(:,:,:), & dt_tg(:,:,:), dt_tracers(:,:,:,nsphum), & - dt_tracers(:,:,:,:), diff_t(:,:,:), & - diff_m(:,:,:), gust(:,:), & - z_pbl(:,:) ) + dt_tracers(:,:,:,:), klcls(:,:), & + do_lcl_diffusivity_depth, diff_t(:,:,:), & + diff_m(:,:,:), gust(:,:), & + z_pbl(:,:) ) - pbltop(is:ie,js:je) = z_pbl(:,:) !s added so that z_pbl can be used subsequently by damping_driver. + pbltop(is:ie,js:je) = z_pbl(:,:) ! added so that z_pbl can be used subsequently by damping_driver. ! !! Don't zero these derivatives as the surface flux depends implicitly @@ -1173,7 +1277,6 @@ subroutine idealized_moist_phys(Time, p_half, p_full, z_half, z_full, ug, vg, tg call error_mesg('atmosphere','no diffusion implentation for non-mixed layer b.c.',FATAL) endif - ! We must use gcm_vert_diff_down and _up rather than gcm_vert_diff as the surface flux ! depends implicitly on the surface values @@ -1202,9 +1305,11 @@ subroutine idealized_moist_phys(Time, p_half, p_full, z_half, z_full, ug, vg, tg ! ! update surface temperature ! - if(mixed_layer_bc) then + if(mixed_layer_bc) then call mixed_layer( & Time, Time+Time_step, & + js, & + je, & t_surf(:,:), & ! t_surf is intent(inout) flux_t(:,:), & flux_q(:,:), & @@ -1231,14 +1336,13 @@ subroutine idealized_moist_phys(Time, p_half, p_full, z_half, z_full, ug, vg, tg endif ! if(turb) then -!s Adding relative humidity calculation so as to allow comparison with Frierson's thesis. +! Adding relative humidity calculation so as to allow comparison with Frierson's thesis. call rh_calc (p_full(:,:,:,previous),tg_tmp,qg_tmp,RH) if(id_rh >0) used = send_data(id_rh, RH*100., Time) - -! RG Add bucket -! Timestepping for bucket. -! NB In tapios github, all physics is still in atmosphere.F90 and this leapfrogging is done there. +! Add bucket +! Timestepping for bucket. +! NB In tapios github, all physics is still in atmosphere.F90 and this leapfrogging is done there. !This part has been included here to avoid editing atmosphere.F90 ! Therefore define a future variable locally, but do not feedback any changes to timestepping variables upstream, so as to avoid messing with the model's overall timestepping. ! Bucket diffusion has been cut for this version - could be incorporated later. @@ -1253,7 +1357,7 @@ subroutine idealized_moist_phys(Time, p_half, p_full, z_half, z_full, ug, vg, tg ! bucket time tendency dt_bucket = depth_change_cond + depth_change_conv - depth_change_lh - !change in bucket depth in one leapfrog timestep [m] + !change in bucket depth in one leapfrog timestep [m] ! use the raw filter in leapfrog time stepping @@ -1265,13 +1369,13 @@ subroutine idealized_moist_phys(Time, p_half, p_full, z_half, z_full, ug, vg, tg *(bucket_depth(:,:,previous) - 2.0*bucket_depth(:,:,current) + bucket_depth(:,:,future)) * raw_bucket else bucket_depth(:,:,current) = bucket_depth(:,:,current ) + robert_bucket & - *(bucket_depth(:,:,previous) - 2.0*bucket_depth(:,:,current)) * raw_bucket + *(bucket_depth(:,:,previous) - 2.0*bucket_depth(:,:,current)) * raw_bucket bucket_depth(:,:,future ) = bucket_depth(:,:,previous) + dt_bucket bucket_depth(:,:,current) = bucket_depth(:,:,current) + robert_bucket * bucket_depth(:,:,future) * raw_bucket endif bucket_depth(:,:,future) = bucket_depth(:,:,future) + robert_bucket * (filt(:,:) + bucket_depth(:,:, future)) & - * (raw_bucket - 1.0) + * (raw_bucket - 1.0) where (bucket_depth <= 0.) bucket_depth = 0. @@ -1288,9 +1392,6 @@ subroutine idealized_moist_phys(Time, p_half, p_full, z_half, z_full, ug, vg, tg endif ! end Add bucket section - - - end subroutine idealized_moist_phys !================================================================================================================================= subroutine idealized_moist_phys_end @@ -1317,11 +1418,10 @@ subroutine idealized_moist_phys_end end subroutine idealized_moist_phys_end !================================================================================================================================= -subroutine rh_calc(pfull,T,qv,RH) !s subroutine copied from 2006 FMS MoistModel file moist_processes.f90 (v14 2012/06/22 14:50:00). +subroutine rh_calc(pfull,T,qv,RH) ! subroutine copied from 2006 FMS MoistModel file moist_processes.f90 (v14 2012/06/22 14:50:00). IMPLICIT NONE - REAL, INTENT (IN), DIMENSION(:,:,:) :: pfull,T,qv REAL, INTENT (OUT), DIMENSION(:,:,:) :: RH @@ -1344,7 +1444,7 @@ subroutine rh_calc(pfull,T,qv,RH) !s subroutine copied from 2006 FMS MoistModel !calculate water saturated vapor pressure from table !and store temporarily in the variable esat - CALL LOOKUP_ES(T,esat) !same as escomp + CALL LOOKUP_ES(T,esat) !same as escomp !calculate denominator in qsat formula if(do_simple) then diff --git a/src/atmos_spectral/driver/solo/mixed_layer.F90 b/src/atmos_spectral/driver/solo/mixed_layer.F90 index 25d9d5072..051d985fe 100644 --- a/src/atmos_spectral/driver/solo/mixed_layer.F90 +++ b/src/atmos_spectral/driver/solo/mixed_layer.F90 @@ -26,7 +26,7 @@ module mixed_layer_mod ! use fms_mod, only: set_domain, write_version_number, & - mpp_pe, mpp_root_pe, error_mesg, FATAL, WARNING + mpp_pe, mpp_root_pe, error_mesg, NOTE, FATAL, WARNING use fms_mod, only: stdlog, check_nml_error, close_file,& open_namelist_file, stdout, file_exist, & @@ -37,20 +37,27 @@ module mixed_layer_mod use tracer_manager_mod, only: get_tracer_names, get_number_tracers -use constants_mod, only: HLV, PI, RHO_CP, CP_AIR +use constants_mod, only: HLV, PI, RHO_CP, CP_AIR, KELVIN use diag_manager_mod, only: register_diag_field, register_static_field, send_data use time_manager_mod, only: time_type +#ifdef COLUMN_MODEL +use column_grid_mod, only: get_deg_lon, get_deg_lat +use column_mod, only: get_surf_geopotential +use spec_mpp_mod, only: grid_domain +#else use transforms_mod, only: get_deg_lat, get_deg_lon, grid_domain +! mj know about surface topography +use spectral_dynamics_mod,only: get_surf_geopotential +#endif use vert_diff_mod, only: surf_diff_type use mpp_domains_mod, only: mpp_get_global_domain !s added to enable qflux reading -! mj know about surface topography -use spectral_dynamics_mod,only: get_surf_geopotential + ! mj read SSTs use interpolator_mod, only: interpolate_type,interpolator_init& &,CONSTANT,interpolator @@ -106,8 +113,11 @@ module mixed_layer_mod logical :: do_qflux = .false. !mj logical :: do_warmpool = .false. !mj logical :: do_read_sst = .false. !mj -logical :: do_sc_sst = .false. !mj +logical :: do_sc_sst = .false. !mj use specified SSTs +logical :: do_ape_sst = .false. ! use the AquaPlanet Experiement (APE) sst profile. logical :: specify_sst_over_ocean_only = .false. +logical :: do_calc_eff_heat_cap = .true. ! assumes specified SST are off the default. + character(len=256) :: sst_file character(len=256) :: land_option = 'none' real,dimension(10) :: slandlon=0,slandlat=0,elandlon=-1,elandlat=-1 @@ -120,6 +130,7 @@ module mixed_layer_mod real :: ice_albedo_value = 0.7 real :: ice_concentration_threshold = 0.5 logical :: update_albedo_from_ice = .false. +character(len=256) :: ice_albedo_method = 'step_function' logical :: add_latent_heat_flux_anom = .false. character(len=256) :: flux_lhe_anom_file_name = 'INPUT/flux_lhe_anom.nc' @@ -129,7 +140,7 @@ module mixed_layer_mod delta_T, prescribe_initial_dist,albedo_value, & land_depth,trop_depth, & !mj trop_cap_limit, heat_cap_limit, np_cap_factor, & !mj - do_qflux,do_warmpool, & !mj + do_qflux,do_warmpool, & !mj albedo_choice,higher_albedo,albedo_exp, & !mj albedo_cntr,albedo_wdth,lat_glacier, & !mj do_read_sst,do_sc_sst,sst_file, & !mj @@ -140,9 +151,9 @@ module mixed_layer_mod load_qflux,qflux_file_name,time_varying_qflux, & update_albedo_from_ice, ice_file_name, & ice_albedo_value, specify_sst_over_ocean_only, & - ice_concentration_threshold, & + ice_concentration_threshold, ice_albedo_method,& add_latent_heat_flux_anom,flux_lhe_anom_file_name,& - flux_lhe_anom_field_name + flux_lhe_anom_field_name, do_ape_sst, qflux_field_name !================================================================================================================================= @@ -218,7 +229,6 @@ subroutine mixed_layer_init(is, ie, js, je, num_levels, t_surf, bucket_depth, ax logical, intent(in), dimension(:,:) :: land logical, intent(in) :: restart_file_bucket_depth - integer :: j real :: rad_qwidth integer:: ierr, io, unit, num_tr, n, global_num_lon, global_num_lat @@ -303,10 +313,10 @@ subroutine mixed_layer_init(is, ie, js, je, num_levels, t_surf, bucket_depth, ax if(land_capacity .le. 0.) land_capacity = depth*RHO_CP !s End MiMA options - !mj read fixed SSTs - if( do_read_sst ) then - call interpolator_init( sst_interp, trim(sst_file)//'.nc', rad_lonb_2d, rad_latb_2d, data_out_of_bounds=(/CONSTANT/) ) - endif + !mj read fixed SSTs + if( do_read_sst ) then + call interpolator_init( sst_interp, trim(sst_file)//'.nc', rad_lonb_2d, rad_latb_2d, data_out_of_bounds=(/CONSTANT/) ) + endif @@ -342,6 +352,10 @@ subroutine mixed_layer_init(is, ie, js, je, num_levels, t_surf, bucket_depth, ax endif +if(trim(ice_albedo_method) == 'ramp_function') then + call error_mesg('mixed_layer','Alternative method ramp_function used for ice albedo output.', NOTE) +endif + id_t_surf = register_diag_field(mod_name, 't_surf', & axes(1:2), Time, 'surface temperature','K') id_flux_t = register_diag_field(mod_name, 'flux_t', & @@ -355,12 +369,12 @@ subroutine mixed_layer_init(is, ie, js, je, num_levels, t_surf, bucket_depth, ax id_delta_t_surf = register_diag_field(mod_name, 'delta_t_surf', & axes(1:2), Time, 'change in sst','K') if (update_albedo_from_ice) then - id_albedo = register_diag_field(mod_name, 'albedo', & + id_albedo = register_diag_field(mod_name, 'albedo', & axes(1:2), Time, 'surface albedo', 'none') - id_ice_conc = register_diag_field(mod_name, 'ice_conc', & + id_ice_conc = register_diag_field(mod_name, 'ice_conc', & axes(1:2), Time, 'ice_concentration', 'none') else - id_albedo = register_static_field(mod_name, 'albedo', & + id_albedo = register_static_field(mod_name, 'albedo', & axes(1:2), 'surface albedo', 'none') endif @@ -369,29 +383,29 @@ subroutine mixed_layer_init(is, ie, js, je, num_levels, t_surf, bucket_depth, ax ! load Q flux if (load_qflux) then - if (time_varying_qflux) then - call interpolator_init( qflux_interp, trim(qflux_file_name)//'.nc', rad_lonb_2d, rad_latb_2d, data_out_of_bounds=(/CONSTANT/) ) - else - - if(file_exist(trim('INPUT/'//qflux_file_name//'.nc'))) then - call mpp_get_global_domain(grid_domain, xsize=global_num_lon, ysize=global_num_lat) - call field_size(trim('INPUT/'//qflux_file_name//'.nc'), trim(qflux_field_name), siz) - if ( siz(1) == global_num_lon .or. siz(2) == global_num_lat ) then - call read_data(trim('INPUT/'//qflux_file_name//'.nc'), trim(qflux_field_name), ocean_qflux, grid_domain) - else - write(ctmp1(1: 4),'(i4)') siz(1) - write(ctmp1(9:12),'(i4)') siz(2) - write(ctmp2(1: 4),'(i4)') global_num_lon - write(ctmp2(9:12),'(i4)') global_num_lat - call error_mesg ('get_qflux','Qflux file contains data on a '// & - ctmp1//' grid, but atmos model grid is '//ctmp2, FATAL) - endif - else - call error_mesg('get_qflux','load_qflux="'//trim('True')//'"'// & - ' but '//trim(qflux_file_name)//' does not exist', FATAL) - endif - - endif + if (time_varying_qflux) then + call interpolator_init( qflux_interp, trim(qflux_file_name)//'.nc', rad_lonb_2d, rad_latb_2d, data_out_of_bounds=(/CONSTANT/) ) + else + + if(file_exist(trim('INPUT/'//qflux_file_name//'.nc'))) then + call mpp_get_global_domain(grid_domain, xsize=global_num_lon, ysize=global_num_lat) + call field_size(trim('INPUT/'//qflux_file_name//'.nc'), trim(qflux_field_name), siz) + if ( siz(1) == global_num_lon .or. siz(2) == global_num_lat ) then + call read_data(trim('INPUT/'//qflux_file_name//'.nc'), trim(qflux_field_name), ocean_qflux, grid_domain) + else + write(ctmp1(1: 4),'(i4)') siz(1) + write(ctmp1(9:12),'(i4)') siz(2) + write(ctmp2(1: 4),'(i4)') global_num_lon + write(ctmp2(9:12),'(i4)') global_num_lat + call error_mesg ('get_qflux','Qflux file contains data on a '// & + ctmp1//' grid, but atmos model grid is '//ctmp2, FATAL) + endif + else + call error_mesg('get_qflux','load_qflux="'//trim('True')//'"'// & + ' but '//trim(qflux_file_name)//' does not exist', FATAL) + endif + + endif endif !s Adding MiMA options for qfluxes. @@ -407,7 +421,7 @@ subroutine mixed_layer_init(is, ie, js, je, num_levels, t_surf, bucket_depth, ax !s End MiMA options for qfluxes if (add_latent_heat_flux_anom) then - call interpolator_init( flux_lhe_anom_interp, trim(flux_lhe_anom_file_name)//'.nc', rad_lonb_2d, rad_latb_2d, data_out_of_bounds=(/CONSTANT/) ) + call interpolator_init( flux_lhe_anom_interp, trim(flux_lhe_anom_file_name)//'.nc', rad_lonb_2d, rad_latb_2d, data_out_of_bounds=(/CONSTANT/) ) endif @@ -455,13 +469,13 @@ subroutine mixed_layer_init(is, ie, js, je, num_levels, t_surf, bucket_depth, ax enddo case (4) ! exponential increase with albedo_exp do j = 1, size(t_surf,2) - lat = abs(deg_lat(js+j-1)) - albedo(:,j) = albedo_value + (higher_albedo-albedo_value)*(lat/90.)**albedo_exp + lat = abs(deg_lat(js+j-1)) + albedo(:,j) = albedo_value + (higher_albedo-albedo_value)*(lat/90.)**albedo_exp enddo case (5) ! tanh increase around albedo_cntr with albedo_wdth do j = 1, size(t_surf,2) - lat = abs(deg_lat(js+j-1)) - albedo(:,j) = albedo_value + (higher_albedo-albedo_value)*& + lat = abs(deg_lat(js+j-1)) + albedo(:,j) = albedo_value + (higher_albedo-albedo_value)*& 0.5*(1+tanh((lat-albedo_cntr)/albedo_wdth)) enddo end select @@ -469,20 +483,39 @@ subroutine mixed_layer_init(is, ie, js, je, num_levels, t_surf, bucket_depth, ax albedo_initial=albedo if (update_albedo_from_ice) then - call interpolator_init( ice_interp, trim(ice_file_name)//'.nc', rad_lonb_2d, rad_latb_2d, data_out_of_bounds=(/CONSTANT/) ) - call read_ice_conc(Time) - call albedo_calc(albedo,Time) + call interpolator_init( ice_interp, trim(ice_file_name)//'.nc', rad_lonb_2d, rad_latb_2d, data_out_of_bounds=(/CONSTANT/) ) + call read_ice_conc(Time) + call albedo_calc(albedo,Time) +else + if ( id_albedo > 0 ) used = send_data ( id_albedo, albedo ) +endif + +! Note: do_calc_eff_heat_cap is true by default and control when the surface +! heat capacity is calculated (land and ocean). +if (do_sc_sst) then + ! if using specified sst then do not calc the heat capacity + if (specify_sst_over_ocean_only) then + ! unless the heat capacity over land is needed + do_calc_eff_heat_cap = .true. + else + do_calc_eff_heat_cap = .false. + endif else - if ( id_albedo > 0 ) used = send_data ( id_albedo, albedo ) + if (do_ape_sst) then + ! if using specified sst without land, do not calc the heat capacity + do_calc_eff_heat_cap = .false. + end if endif + + !s begin surface heat capacity calculation - if(.not.do_sc_sst.or.(do_sc_sst.and.specify_sst_over_ocean_only)) then - land_sea_heat_capacity = depth*RHO_CP - if(trim(land_option) .ne. 'input') then +if (do_calc_eff_heat_cap) then + land_sea_heat_capacity = depth*RHO_CP + if(trim(land_option) .ne. 'input') then if ( trop_capacity .ne. depth*RHO_CP .or. np_cap_factor .ne. 1. ) then !s Lines above make trop_capacity=depth*RHO_CP if trop_capacity set to be < 0. do j=js,je - lat = deg_lat(j) + lat = deg_lat(j) if ( lat .gt. 0. ) then loc_cap = depth*RHO_CP*np_cap_factor else @@ -505,7 +538,7 @@ subroutine mixed_layer_init(is, ie, js, je, num_levels, t_surf, bucket_depth, ax ! mj land heat capacity given through ?landlon, ?landlat if(trim(land_option) .eq. 'lonlat')then do j=js,je - lat = deg_lat(j) + lat = deg_lat(j) do i=is,ie lon = deg_lon(i) do k=1,size(slandlat) @@ -517,10 +550,10 @@ subroutine mixed_layer_init(is, ie, js, je, num_levels, t_surf, bucket_depth, ax enddo enddo endif - else !trim(land_option) .eq. 'input' - where(land) land_sea_heat_capacity = land_h_capacity_prefactor*land_sea_heat_capacity - endif !end of if (trim(land_option) .ne. 'input') - endif !end of if(.not.do_sc_sst) + else !trim(land_option) .eq. 'input' + where(land) land_sea_heat_capacity = land_h_capacity_prefactor*land_sea_heat_capacity + endif !end of if (trim(land_option) .ne. 'input') +endif !end of if(.not.do_sc_sst) if ( id_heat_cap > 0 ) used = send_data ( id_heat_cap, land_sea_heat_capacity ) !s end surface heat capacity calculation @@ -535,6 +568,7 @@ end subroutine mixed_layer_init subroutine mixed_layer ( & Time, & Time_next, & + js, je, & t_surf, & flux_t, & flux_q, & @@ -552,33 +586,34 @@ subroutine mixed_layer ( & albedo_out) ! ---- arguments ----------------------------------------------------------- -type(time_type), intent(in) :: Time, Time_next -real, intent(in), dimension(:,:) :: & - net_surf_sw_down, surf_lw_down -real, intent(in), dimension(:,:) :: & - flux_t, flux_q, flux_r +type(time_type), intent(in) :: Time, Time_next +integer, intent(in) :: js, je +real, intent(in), dimension(:,:) :: net_surf_sw_down, surf_lw_down +real, intent(in), dimension(:,:) :: flux_t, flux_q, flux_r real, intent(inout), dimension(:,:) :: t_surf -real, intent(in), dimension(:,:) :: & - dhdt_surf, dedt_surf, dedq_surf, & - drdt_surf, dhdt_atm, dedq_atm -real, intent(in) :: dt -real, intent(out), dimension(:,:) :: albedo_out +real, intent(in), dimension(:,:) :: dhdt_surf, dedt_surf, dedq_surf, & + drdt_surf, dhdt_atm, dedq_atm +real, intent(in) :: dt +real, intent(out), dimension(:,:) :: albedo_out type(surf_diff_type), intent(inout) :: Tri_surf logical, dimension(size(land_mask,1),size(land_mask,2)) :: land_ice_mask +!local variables + +integer :: j if(.not.module_is_initialized) then call error_mesg('mixed_layer','mixed_layer module is not initialized',FATAL) endif if(update_albedo_from_ice) then - call read_ice_conc(Time_next) - land_ice_mask=.false. - where(land_mask.or.(ice_concentration.gt.ice_concentration_threshold)) - land_ice_mask=.true. - end where + call read_ice_conc(Time_next) + land_ice_mask=.false. + where(land_mask.or.(ice_concentration.gt.ice_concentration_threshold)) + land_ice_mask=.true. + end where else - land_ice_mask=land_mask + land_ice_mask=land_mask endif call albedo_calc(albedo_out,Time_next) @@ -624,9 +659,9 @@ subroutine mixed_layer ( & if(load_qflux.and.time_varying_qflux) then call interpolator( qflux_interp, Time, ocean_qflux, trim(qflux_file_name) ) - if(update_albedo_from_ice) then - where (land_ice_mask) ocean_qflux=0. - endif + if(update_albedo_from_ice) then + where (land_ice_mask) ocean_qflux=0. + endif endif @@ -644,37 +679,62 @@ subroutine mixed_layer ( & !s Surface heat_capacity calculation based on that in MiMA by mj if(do_sc_sst) then !mj sst read from input file - ! read at the new time, as that is what we are stepping to - call interpolator( sst_interp, Time_next, sst_new, trim(sst_file) ) - - if(specify_sst_over_ocean_only) then - where (.not.land_ice_mask) delta_t_surf = sst_new - t_surf - where (.not.land_ice_mask) t_surf = t_surf + delta_t_surf - else - delta_t_surf = sst_new - t_surf - t_surf = t_surf + delta_t_surf - endif - + ! read at the new time, as that is what we are stepping to + call interpolator( sst_interp, Time_next, sst_new, trim(sst_file) ) + + if(specify_sst_over_ocean_only) then + where (.not.land_ice_mask) delta_t_surf = sst_new - t_surf + where (.not.land_ice_mask) t_surf = t_surf + delta_t_surf + else + delta_t_surf = sst_new - t_surf + t_surf = t_surf + delta_t_surf + endif end if -if ((.not.do_sc_sst).or.(do_sc_sst.and.specify_sst_over_ocean_only)) then +if (do_ape_sst) then + ! + ! AquaPlanet Experiment protocol (APE) from + ! Williams et al 2012 tech report: "The APE Atlas" + ! + ! use analytic form for setting SST at each timestep. + ! see appendix equation 1 of Neale and Hoskins 2000 + ! "A standard test for AGCMs including their + ! physical parametrizations: I: the proposal" + ! using a constant longitude. + do j=js,je + if ( (rad_lat_2d(1,j) .gt. -PI/3.) .and. (rad_lat_2d(1,j) .lt. PI/3.) ) then + ! between 60N-60S + sst_new(:,j) = KELVIN+( 27.0*( 1. - (sin( 3./2. * rad_lat_2d(:,j) )**2 ) )) + !write(6,*) 'SST profile', rad_lat_2d(1,j)*180/PI, ' j:', j, ' sst:', sst_new(1,j) + else + ! from 60N/S to pole + sst_new(:,j) = KELVIN + !write(6,*) 'SST is zero', rad_lat_2d(1,j)*180/PI, ' j:', j, ' sst:', sst_new(1,j) + endif + enddo + delta_t_surf = sst_new - t_surf + t_surf = sst_new +endif + + +if (do_calc_eff_heat_cap) then !s use the land_sea_heat_capacity calculated in mixed_layer_init - ! Now update the mixed layer surface temperature using an implicit step - ! - eff_heat_capacity = land_sea_heat_capacity + t_surf_dependence * dt !s need to investigate how this works + ! Now update the mixed layer surface temperature using an implicit step + ! + eff_heat_capacity = land_sea_heat_capacity + t_surf_dependence * dt !s need to investigate how this works - if (any(eff_heat_capacity .eq. 0.0)) then - write(*,*) 'mixed_layer: error', eff_heat_capacity - call error_mesg('mixed_layer', 'Avoiding division by zero',fatal) - end if + if (any(eff_heat_capacity .eq. 0.0)) then + write(*,*) 'mixed_layer: error', eff_heat_capacity + call error_mesg('mixed_layer', 'Avoiding division by zero',fatal) + end if if(do_sc_sst.and.specify_sst_over_ocean_only) then where (land_ice_mask) delta_t_surf = - corrected_flux * dt / eff_heat_capacity - where (land_ice_mask) t_surf = t_surf + delta_t_surf + where (land_ice_mask) t_surf = t_surf + delta_t_surf else delta_t_surf = - corrected_flux * dt / eff_heat_capacity - t_surf = t_surf + delta_t_surf + t_surf = t_surf + delta_t_surf endif endif !s end of if(do_sc_sst). @@ -709,12 +769,19 @@ subroutine albedo_calc(albedo_inout,Time) if(update_albedo_from_ice) then - where(ice_concentration.gt.ice_concentration_threshold) - albedo_inout=ice_albedo_value - end where - - if ( id_ice_conc > 0 ) used = send_data ( id_ice_conc, ice_concentration, Time ) - if ( id_albedo > 0 ) used = send_data ( id_albedo, albedo_inout, Time ) + if(trim(ice_albedo_method) == 'step_function') then + where(ice_concentration.gt.ice_concentration_threshold) + albedo_inout=ice_albedo_value + end where + else if(trim(ice_albedo_method) == 'ramp_function') then + albedo_inout = albedo_inout*(1.0-ice_concentration) + ice_albedo_value*ice_concentration + else + call error_mesg('mixed_layer','"'//trim(ice_albedo_method)//'"'//' is not a valid method for determining'// & + 'albedo when ice is present. Choices are: step_function or ramp_function.', FATAL) + endif + + if ( id_ice_conc > 0 ) used = send_data ( id_ice_conc, ice_concentration, Time ) + if ( id_albedo > 0 ) used = send_data ( id_albedo, albedo_inout, Time ) endif diff --git a/src/atmos_spectral/model/spectral_dynamics.F90 b/src/atmos_spectral/model/spectral_dynamics.F90 index 7bda2f077..ab2e0b9ae 100644 --- a/src/atmos_spectral/model/spectral_dynamics.F90 +++ b/src/atmos_spectral/model/spectral_dynamics.F90 @@ -1668,7 +1668,7 @@ subroutine spectral_diagnostics_init(Time) 'height', axes_3d_full, Time, 'geopotential height at full model levels','m') id_uz = register_diag_field(mod_name, & - 'ucomp_height',axes_3d_full, Time, 'zonal wind * geopotential height at full model levels', 'm**2sec') + 'ucomp_height',axes_3d_full, Time, 'zonal wind * geopotential height at full model levels', 'm**2/sec') id_vz = register_diag_field(mod_name, & 'vcomp_height',axes_3d_full, Time, 'meridional wind * geopotential height at full model levels', 'm**2/sec') diff --git a/src/atmos_spectral_barotropic/atmosphere.F90 b/src/atmos_spectral_barotropic/atmosphere.F90 new file mode 100644 index 000000000..6b878fe98 --- /dev/null +++ b/src/atmos_spectral_barotropic/atmosphere.F90 @@ -0,0 +1,250 @@ +module atmosphere_mod + +!----------------------------------------------------------------------- +! GNU General Public License +! +! This program is free software; you can redistribute it and/or modify it and +! are expected to follow the terms of the GNU General Public License +! as published by the Free Software Foundation; either version 2 of +! the License, or (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, but WITHOUT +! ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +! or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +! License for more details. +! +! For the full text of the GNU General Public License, +! write to: Free Software Foundation, Inc., +! 675 Mass Ave, Cambridge, MA 02139, USA. +! or see: http://www.gnu.org/licenses/gpl.html +!----------------------------------------------------------------------- + +use fms_mod, only: open_namelist_file, & + open_restart_file, & + file_exist, & + check_nml_error, & + error_mesg, & + FATAL, WARNING, & + write_version_number, & + mpp_pe, & + mpp_root_pe, & + close_file, & + stdlog, stdout + +use transforms_mod, only: get_deg_lon, & + get_deg_lat, & + get_grid_boundaries, & + get_grid_domain, & + get_spec_domain, & + area_weighted_global_mean, & + atmosphere_domain + +use time_manager_mod, only: time_type, & + set_time, & + get_time, & + interval_alarm, & + operator(+), & + operator(<), & + operator(==) + +use barotropic_dynamics_mod, only: barotropic_dynamics_init, & + barotropic_dynamics, & + barotropic_dynamics_end, & + dynamics_type + +use barotropic_physics_mod, only: barotropic_physics_init, & + barotropic_physics, & + barotropic_physics_end, & + phys_type + +use diag_manager_mod, only: diag_manager_init, & + diag_manager_end + +use barotropic_diagnostics_mod, only: barotropic_diagnostics_init, & + barotropic_diagnostics + +use stirring_mod, only: stirring_init + + +!======================================================================== +implicit none +private +!======================================================================== + +! version information +!======================================================================== +character(len=128) :: version = '$Id: atmosphere.F90,v 17.0 2009/07/21 03:00:15 fms Exp $' +character(len=128) :: tagname = '$Name: siena_201207 $' +!======================================================================== + +public :: atmosphere_init, & + atmosphere, & + atmosphere_end, & + atmosphere_domain + +!======================================================================== + +integer :: unit, seconds, days +integer :: pe, npes, itmp, m, n +integer :: previous, current, future +logical :: root + +integer :: dt_integer +real :: dt_real +type(time_type) :: dt_time_type, Time_init, Time_step + +real :: delta_t ! = 2*dt_real for leapfrog step + +type(phys_type), save :: Phys +type(dynamics_type), save :: Dyn + +integer :: is, ie, js, je, ms, me, ns, ne +integer :: num_lon, num_lat + +logical :: module_is_initialized =.false. + +integer :: print_interval=86400 + +! namelist +!======================================================================== +namelist /atmosphere_nml/ print_interval +!======================================================================== + +contains +!======================================================================= + +subroutine atmosphere_init(Time_init_in, Time, Time_step_in) + +type (time_type), intent(in) :: Time_init_in, Time, Time_step_in + +integer :: i, j, n, nn, ierr, io, unit +integer :: nlon, nlat +integer :: id_lon, id_lat, id_lonb, id_latb + +pe = mpp_pe() +root = (pe == mpp_root_pe()) + +Time_step = Time_step_in +call get_time( Time_step, seconds, days) +dt_integer = 86400*days + seconds +dt_real = float(dt_integer) +dt_time_type = Time_step +Time_init = Time_init_in + +! read the namelist + +if (file_exist('input.nml')) then + unit = open_namelist_file () + ierr=1 + do while (ierr /= 0) + read (unit, nml=atmosphere_nml, iostat=io, end=10) + ierr = check_nml_error (io, 'atmosphere_nml') + enddo + 10 call close_file (unit) +endif +call write_version_number(version, tagname) +if (root) write (stdlog(), nml=atmosphere_nml) + +call barotropic_dynamics_init (Dyn, Time, Time_init, dt_real) + +call get_grid_domain(is,ie,js,je) +call get_spec_domain(ms,me,ns,ne) + +num_lon = Dyn%num_lon +num_lat = Dyn%num_lat + +nlon = ie+1-is ! size of grid on each processor +nlat = je+1-js + +call barotropic_physics_init(Phys) +call barotropic_diagnostics_init(Time, num_lon, num_lat, id_lon, id_lat, id_lonb, id_latb) +call stirring_init(dt_real, Time, id_lon, id_lat, id_lonb, id_latb) + +if(Time == Time_init) then + previous = 1 + current = 1 ! starting with a forward step before settling into leapfrog +else + previous = 1 + current = 2 +endif + +module_is_initialized = .true. + +return +end subroutine atmosphere_init + +!===================================================================== + +subroutine atmosphere(Time) + +type (time_type), intent(in) :: Time +integer :: day, second, dt +real :: energy, enstrophy + +if(.not.module_is_initialized) then + call error_mesg('atmosphere', & + 'atmosphere_init has not been called', FATAL) +end if + +call get_time(Time_step, second, day) +dt = second + 86400*day + +Dyn%Tend%u = 0.0 +Dyn%Tend%v = 0.0 +if(Dyn%grid_tracer) Dyn%Tend%tr = 0.0 +if(Dyn%spec_tracer) Dyn%Tend%trs = 0.0 + +if(Time == Time_init) then + delta_t = dt_real + future = 2 +else + delta_t = 2.0*dt_real + future = previous +endif + +call barotropic_physics(Time, & + Dyn%Tend%u, Dyn%Tend%v, & + Dyn%Grid%u, Dyn%Grid%v, & + delta_t, previous, current, & + Phys) + +call barotropic_dynamics(Time, Time_init, & + Dyn, previous, current, future, delta_t) + +previous = current +current = future + +call barotropic_diagnostics (Time+Time_step, Dyn, Phys, current) + +enstrophy = area_weighted_global_mean(Dyn%grid%vor(:,:,current)*Dyn%grid%vor(:,:,previous)) +energy = -area_weighted_global_mean(Dyn%grid%stream*Dyn%grid%vor(:,:,previous)) + +if(root) then + call get_time(Time+Time_step, second, day) + if(mod(second+86400*day, print_interval) < dt) then + write(stdout(),1000) day, second, energy, enstrophy + end if +end if +1000 format(1x, 'day =',i6,2x,'second =', i6,2x,'energy = ',e13.6,3x,'enstrophy = ',e13.6) + +return +end subroutine atmosphere + +!======================================================================================= + +subroutine atmosphere_end + +if(.not.module_is_initialized) then + call error_mesg('atmosphere_end', & + 'atmosphere_init has not been called.', FATAL) +end if + +call barotropic_physics_end (Phys) +call barotropic_dynamics_end (Dyn, previous, current) +module_is_initialized = .false. + +return +end subroutine atmosphere_end + +!======================================================================================= +end module atmosphere_mod diff --git a/src/atmos_spectral_barotropic/atmosphere.html b/src/atmos_spectral_barotropic/atmosphere.html new file mode 100644 index 000000000..7b937f1f1 --- /dev/null +++ b/src/atmos_spectral_barotropic/atmosphere.html @@ -0,0 +1,174 @@ + +module atmosphere_mod + + +
+ +

module atmosphere_mod

+ + +
+     Contact: Isaac Held
+     Reviewers: Peter Phillipps
+
+
+ + +
+

OVERVIEW

+ +
+   A spectral transform model for two-dimensional, non-divergent flow on the
+   surface of the sphere.  
+
+
+ + +
+

DESCRIPTION

+ +
+   Integrates the barotropic vorticity equation for nondivergent flow on the
+   sphere using the spectral transform technique.  Also allows for the
+   inclusion of a passive tracer advected by the same spectral advection
+   algorithm as  the vorticity, and a gridpoint tracer advected with a finite
+   volume  algorithm on the transform grid.  The default initial condition 
+   provided as an example is a zonal flow resembling that in the Northern
+   winter,  plus a sinusoidal disurbance localized in midlatitudes.
+
+   For a full description of the model and algorithms used, see barotropic.ps
+
+   The interfaces in this module are the generic intefaces required by the
+   main program that can be used to drive various idealized atmospheric
+   models within FMS. Model resolution and related paramters are set in
+   namelists within the modules barotropic_xxx.
+
+ + + +
+

OTHER MODULES USED

+ +
+     fms_mod
+     constants_mod
+     transforms_mod
+     time_manager_mod
+     diag_manager_mod
+     barotropic_dynamics_mod
+     barotropic_physics_mod
+     barotopic_diagnostics_mod
+
+
+ + +
+

PUBLIC INTERFACE

+ +
+
+  use atmosphere_mod [,only: atmosphere_init,       
+                             atmosphere,
+			     atmosphere_end]
+                                
+
+
+ + +
+

PUBLIC DATA

+ +
+     
+  There are no pubic data types
+ 
+
+
+ + +
+

PUBLIC ROUTINES

+ +
+
+subroutine atmosphere_init. Initializes the model.
+subroutine atmosphere.      Integrates forward one time step
+subroutine atmosphere_end.  Terminates model, cleaning up memory and finalizing diagnostics.
+
+
+
+
+ subroutine atmosphere_init(Time_init, Time, Time_step)
+
+    input:
+ 
+    type(time_type) :: Time_init -- Initial model time
+
+    type(time_type) :: Time      -- Model time
+
+    type(time_type) :: Time_step -- Time step
+       
+    When Time=Time_init, the first time step is a forward
+    step rather than leap frog because a cold start is assumed.
+
+    The FMS main program that runs the solo atmospheric models
+    obtains Time_init from the diag_table and Time from its namelist.
+
+
+
+ + + subroutine atmosphere(Time) + + input: + + type(time_type) :: Time -- Model time + + Integrates forward one time step + + +
+ + subroutine atmosphere_end(Atmos) + + No calling arguments. + + Terminates model, cleaning up memory and finalizing diagnostics + + +
+
+ + +

NAMELIST

+ +
+&atmosphere_nml
+
+   print_interval, integer : time interval in seconds 
+   between prints of global mean energy and enstrophy to standard output
+
+
+ + +
+

ERROR MESSAGES

+ +
+
+  Fatal error message if any public routine is called prior to atmosphere_init
+
+
+
+ + +
+ + diff --git a/src/atmos_spectral_barotropic/barotropic.pdf b/src/atmos_spectral_barotropic/barotropic.pdf new file mode 100644 index 000000000..454e56389 Binary files /dev/null and b/src/atmos_spectral_barotropic/barotropic.pdf differ diff --git a/src/atmos_spectral_barotropic/barotropic_diagnostics.F90 b/src/atmos_spectral_barotropic/barotropic_diagnostics.F90 new file mode 100644 index 000000000..fa632893a --- /dev/null +++ b/src/atmos_spectral_barotropic/barotropic_diagnostics.F90 @@ -0,0 +1,174 @@ + +module barotropic_diagnostics_mod + +!----------------------------------------------------------------------- +! GNU General Public License +! +! This program is free software; you can redistribute it and/or modify it and +! are expected to follow the terms of the GNU General Public License +! as published by the Free Software Foundation; either version 2 of +! the License, or (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, but WITHOUT +! ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +! or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +! License for more details. +! +! For the full text of the GNU General Public License, +! write to: Free Software Foundation, Inc., +! 675 Mass Ave, Cambridge, MA 02139, USA. +! or see: http://www.gnu.org/licenses/gpl.html +!----------------------------------------------------------------------- + +use fms_mod, only: write_version_number, & + mpp_pe, & + mpp_root_pe, & + error_mesg, FATAL + +use transforms_mod, only: get_grid_domain, & + get_spec_domain, & + grid_domain, & + trans_spherical_to_grid, & + get_deg_lon, & + get_deg_lat, & + get_grid_boundaries + +use diag_manager_mod, only: register_diag_field, & + register_static_field, & + send_data, & + diag_axis_init + +use time_manager_mod, only: time_type, & + get_time + +use barotropic_physics_mod, only: phys_type +use barotropic_dynamics_mod, only: grid_type, dynamics_type + +implicit none +private + +public :: barotropic_diagnostics_init, & + barotropic_diagnostics + +character(len=84), parameter :: version = '$Id: barotropic_diagnostics.F90,v 17.0 2009/07/21 03:00:18 fms Exp $' +character(len=84), parameter :: tagname = '$Name: siena_201207 $' + +logical :: module_is_initialized = .false. + +character(len=8) :: axiset = 'barotropic' +character(len=84) :: mod_name = 'barotropic_diagnostics' + +integer :: id_vor, id_stream, id_pv, id_u, id_v, id_trs, id_tr, id_eddy_vor, id_delta_u +integer :: is, ie, js, je, ms, me, ns, ne + +contains + +!----------------------------------------------------------------------------------------------------------------- +subroutine barotropic_diagnostics_init(Time, lon_max, lat_max, id_lon, id_lat, id_lonb, id_latb) + +type(time_type), intent(in) :: Time +integer, intent(in) :: lon_max, lat_max +integer, intent(out):: id_lon, id_lat, id_lonb, id_latb + +real, dimension(lon_max ) :: lon +real, dimension(lon_max+1) :: lonb +real, dimension(lat_max ) :: lat +real, dimension(lat_max+1) :: latb + +integer, dimension(2) :: axis_2d + +real :: rad_to_deg + +integer :: log_unit +integer :: namelist_unit, ierr, io +logical :: used + +call write_version_number(version, tagname) + +call get_grid_domain(is, ie, js, je) +call get_spec_domain(ms, me, ns, ne) + +call get_deg_lon(lon) +call get_deg_lat(lat) +call get_grid_boundaries(lonb,latb,global=.true.) + +rad_to_deg = 45./atan(1.) +id_lonb=diag_axis_init('lonb', rad_to_deg*lonb, 'degrees_E', 'x', 'longitude edges', set_name=axiset, Domain2=grid_domain) +id_latb=diag_axis_init('latb', rad_to_deg*latb, 'degrees_N', 'y', 'latitude edges', set_name=axiset, Domain2=grid_domain) +id_lon =diag_axis_init('lon', lon, 'degrees_E', 'x', 'longitude', set_name=axiset, Domain2=grid_domain, edges=id_lonb) +id_lat =diag_axis_init('lat', lat, 'degrees_N', 'y', 'latitude', set_name=axiset, Domain2=grid_domain, edges=id_latb) + +axis_2d(1) = id_lon +axis_2d(2) = id_lat + +id_u = register_diag_field(mod_name, 'ucomp' , axis_2d, Time, 'u_wind' , 'm/s' ) +id_v = register_diag_field(mod_name, 'vcomp' , axis_2d, Time, 'v_wind' , 'm/s' ) +id_vor = register_diag_field(mod_name, 'vor' , axis_2d, Time, 'relative vorticity', '1/s' ) +id_pv = register_diag_field(mod_name, 'pv' , axis_2d, Time, 'absolute vorticity', '1/s' ) +id_stream = register_diag_field(mod_name, 'stream' , axis_2d, Time, 'streamfunction' , 'm^2/s') +id_trs = register_diag_field(mod_name, 'trs' , axis_2d, Time, 'spectral tracer' , 'none' ) +id_tr = register_diag_field(mod_name, 'tr' , axis_2d, Time, 'grid tracer' , 'none' ) +id_eddy_vor= register_diag_field(mod_name, 'eddy_vor', axis_2d, Time, 'eddy vorticity' , '1/s' ) +id_delta_u = register_diag_field(mod_name, 'delta_u' , axis_2d, Time, 'change in zonal wind','m/s' ) + +module_is_initialized = .true. + +return +end subroutine barotropic_diagnostics_init + +!-------------------------------------------------------------------------------------------- + +subroutine barotropic_diagnostics(Time, Dyn, Phys, time_index) + +type(time_type), intent(in) :: Time +type(phys_type), intent(in) :: Phys +type(dynamics_type), intent(in) :: Dyn +integer, intent(in) :: time_index + +real, dimension(is:ie,js:je) :: grid_tmp +real, dimension(is:ie,js:je) :: delta_zonal_u +complex, dimension(ms:me,ns:ne) :: spec_tmp +logical :: used +integer :: j + +if(.not.module_is_initialized) call error_mesg('barotropic_diagnostics', & + 'barotropic_diagnostics_init has not been called', FATAL) + +if(id_u > 0) used = send_data(id_u , Dyn%Grid%u (:,:,time_index) , time) +if(id_v > 0) used = send_data(id_v , Dyn%Grid%v (:,:,time_index) , time) +if(id_vor > 0) used = send_data(id_vor , Dyn%Grid%vor (:,:,time_index) , time) +if(id_pv > 0) used = send_data(id_pv , Dyn%Grid%pv (:,:) , time) +if(id_stream > 0) used = send_data(id_stream , Dyn%Grid%stream (:,:) , time) +if(id_tr > 0) used = send_data(id_tr , Dyn%Grid%tr (:,:,time_index) , time) +if(id_trs > 0) used = send_data(id_trs , Dyn%Grid%trs (:,:,time_index) , time) +if(id_eddy_vor > 0) then + spec_tmp = Dyn%Spec%vor(:,:,time_index) + if(ms == 0) spec_tmp(0,:) = cmplx(0.0,0.0) + call trans_spherical_to_grid(spec_tmp, grid_tmp) + used = send_data(id_eddy_vor, grid_tmp, time) +endif +if(id_delta_u > 0) then + do j=js,je + delta_zonal_u(:,j) = sum(Dyn%Grid%u(:,j,time_index))/Dyn%num_lon - Dyn%Grid%zonal_u_init(j) + enddo + used = send_data(id_delta_u, delta_zonal_u, time) +endif + +return +end subroutine barotropic_diagnostics +!-------------------------------------------------------------------------------------------- + +subroutine barotropic_diagnostics_end(Time) + +type(time_type), intent(in) :: Time + +if(.not.module_is_initialized) call error_mesg('barotropic_diagnostics_end', & + 'barotropic_diagnostics_init has not been called', FATAL) + +module_is_initialized = .false. + +return +end subroutine barotropic_diagnostics_end +!-------------------------------------------------------------------------------------------- + +end module barotropic_diagnostics_mod diff --git a/src/atmos_spectral_barotropic/barotropic_diagnostics.html b/src/atmos_spectral_barotropic/barotropic_diagnostics.html new file mode 100644 index 000000000..d852a921d --- /dev/null +++ b/src/atmos_spectral_barotropic/barotropic_diagnostics.html @@ -0,0 +1,152 @@ + +module barotropic_diagnostics_mod + + +
+ + +

module barotropic_diagnostics_mod

+ + +
+     Contact: Isaac Held
+     Reviewers: Peter Phillipps
+
+
+
+ + +
+

OVERVIEW

+ +
+
+   The diagnostics module for the model that solves the barotropic vorticity
+   equation on the sphere  
+   
+
+
+ + +
+

DESCRIPTION

+ +
+
+   Using the diagnostics manager, creates output files for the barotropic model.
+   Variables currently available for output are
+       zonal wind 
+       meridional wind 
+       relative vorticity
+       absolute vorticity
+       streamfunction
+       spectral tracer in grid domain
+       grid tracer
+       
+   Whether or not these fields are actually output, the location of the 
+   output, the frequency of output, whether or not the output is averaged
+   in time or an instantaneous snapshot, is controlled by a 
+   diag_table file utilized by the diagnostics manager module
+       
+   One can add other diagnostics by following the (somewhat convoluted)
+       pattern within the program
+
+
+
+
+ + +
+

OTHER MODULES USED

+ +
+
+     diag_manaager_mod
+     transforms_mod
+     time_manager_mod
+     barotropic_dynamics_mod
+     barotropic_physics_mod
+
+
+
+ + +
+

PUBLIC INTERFACE

+ +
+
+  use barotropic_diagnostics_mod [,only: barotropic_diagnostics_init,       
+                                         barotropic_diagnostics]
+                                
+
+
+ + + +
+

PUBLIC ROUTINES

+ +
+
+subroutine  barotropic_diagnostics_init
+subroutine  barotropic_diagnostics
+
+
+
+
+ subroutine barotropic_diagnostics_init(Time, num_lon, num_lat)
+ 
+   type(time_type)    , intent(in)     :: Time
+         current time 
+   integer, intent(in) :: num_lon, num_lat
+      num_lon = number of longitudes in global domain
+      num_lat = number of latitudes in global domain
+         
+
+   Initializes module
+
+
+
+
+ + + + subroutine barotropic_diagnostics (Time, Grid, Phys, time_index) + + type(time_type), intent(in) :: Time + type(phys_type), intent(in) :: Phys + type(grid_type), intent(in) :: Grid + integer, intent(in) :: time_index + + phys_type is defined in barotropic_physics_mod; + Phys is currently empty as there is no information generated in + barotropic_physics_mod to be output; + + grid_type is defined in barotropic_dynamics_mod: + Grid contains all of the fields to be output + + many of the grid fields in grid_type are dimensioned (lon, lat, time_index) + where time_index = 1 or 2 -- the two time levels needed to update the + state of the model using a leapfrog step are toggled between (:,:,1) + and (:,:,2). The input time_index (which must equal either 1 or 2) + determines which of these two fields is output) + + (this is confusing -- the calling program needs to know what has + been placed in which slot -- it would be better to store this + information within the data type) + + + + +
+ + + diff --git a/src/atmos_spectral_barotropic/barotropic_dynamics.F90 b/src/atmos_spectral_barotropic/barotropic_dynamics.F90 new file mode 100644 index 000000000..ac18b6127 --- /dev/null +++ b/src/atmos_spectral_barotropic/barotropic_dynamics.F90 @@ -0,0 +1,574 @@ +module barotropic_dynamics_mod + +!----------------------------------------------------------------------- +! GNU General Public License +! +! This program is free software; you can redistribute it and/or modify it and +! are expected to follow the terms of the GNU General Public License +! as published by the Free Software Foundation; either version 2 of +! the License, or (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, but WITHOUT +! ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +! or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +! License for more details. +! +! For the full text of the GNU General Public License, +! write to: Free Software Foundation, Inc., +! 675 Mass Ave, Cambridge, MA 02139, USA. +! or see: http://www.gnu.org/licenses/gpl.html +!----------------------------------------------------------------------- + +use fms_mod, only: open_namelist_file, & + open_restart_file, & + file_exist, & + check_nml_error, & + error_mesg, & + mpp_error, & + FATAL, WARNING, & + write_version_number, & + mpp_pe, & + mpp_root_pe, & + read_data, & + write_data, & + set_domain, & + close_file, & + stdlog + +use time_manager_mod, only : time_type, & + get_time, & + operator(==), & + operator(-) + +use constants_mod, only: radius, omega + +use transforms_mod, only: transforms_init, transforms_end, & + get_grid_boundaries, & + trans_spherical_to_grid, trans_grid_to_spherical, & + compute_laplacian, & + get_sin_lat, get_cos_lat, & + get_deg_lon, get_deg_lat, & + get_grid_domain, get_spec_domain, & + spectral_domain, grid_domain, & + vor_div_from_uv_grid, uv_grid_from_vor_div, & + horizontal_advection + +use spectral_damping_mod, only: spectral_damping_init, & + compute_spectral_damping + +use leapfrog_mod, only: leapfrog + +use fv_advection_mod, only: fv_advection_init, & + a_grid_horiz_advection + +use stirring_mod, only: stirring, stirring_end + +!=============================================================================================== +implicit none +private +!=============================================================================================== + +public :: barotropic_dynamics_init, & + barotropic_dynamics, & + barotropic_dynamics_end, & + dynamics_type, & + grid_type, & + spectral_type, & + tendency_type + + +! version information +!=================================================================== +character(len=128) :: version = '$Id: barotropic_dynamics.F90,v 17.0 2009/07/21 03:00:21 fms Exp $' +character(len=128) :: tagname = '$Name: siena_201207 $' +!=================================================================== + +type grid_type + real, pointer, dimension(:,:,:) :: u=>NULL(), v=>NULL(), vor=>NULL(), trs=>NULL(), tr=>NULL() + real, pointer, dimension(:,:) :: pv=>NULL(), stream=>NULL() + real, pointer, dimension(:) :: zonal_u_init=>NULL() +end type +type spectral_type + complex, pointer, dimension(:,:,:) :: vor=>NULL(), trs=>NULL() +end type +type tendency_type + real, pointer, dimension(:,:) :: u=>NULL(), v=>NULL(), trs=>NULL(), tr=>NULL() +end type +type dynamics_type + type(grid_type) :: grid + type(spectral_type) :: spec + type(tendency_type) :: tend + integer :: num_lon, num_lat + logical :: grid_tracer, spec_tracer +end type + +integer, parameter :: num_time_levels = 2 + +integer :: is, ie, js, je, ms, me, ns, ne + +logical :: module_is_initialized = .false. + +real, allocatable, dimension(:) :: sin_lat, cos_lat, rad_lat, rad_lon, & + deg_lat, deg_lon, & + coriolis, glon_bnd, glat_bnd + +integer :: pe, npes + +! namelist parameters with default values + +logical :: check_fourier_imag = .false. +logical :: south_to_north = .true. +logical :: triang_trunc = .true. + +real :: robert_coeff = 0.04 +real :: longitude_origin = 0.0 +real :: raw_filter_coeff = 1.0 + +character(len=64) :: damping_option = 'resolution_dependent' +integer :: damping_order = 4 +real :: damping_coeff = 1.e-04 +real :: damping_coeff_r = 0.0 + +real :: zeta_0 = 8.e-05 +integer :: m_0 = 4 +real :: eddy_width = 15.0 +real :: eddy_lat = 45.0 + +logical :: spec_tracer = .true. +logical :: grid_tracer = .true. + +integer :: num_lat = 128 +integer :: num_lon = 256 +integer :: num_fourier = 85 +integer :: num_spherical = 86 +integer :: fourier_inc = 1 +integer :: cutoff_wn = 30 + +real, dimension(2) :: valid_range_v = (/-1.e3,1.e3/) +character(len=64) :: initial_zonal_wind = 'two_jets' + +namelist /barotropic_dynamics_nml/ check_fourier_imag, south_to_north, & + triang_trunc, & + num_lon, num_lat, num_fourier, & + num_spherical, fourier_inc, & + longitude_origin, damping_option, & + damping_order, damping_coeff, & + damping_coeff_r, robert_coeff, & + spec_tracer, grid_tracer, & + eddy_lat, eddy_width, zeta_0, m_0, & + valid_range_v, initial_zonal_wind, & + cutoff_wn + +contains + +!=============================================================================================== + +subroutine barotropic_dynamics_init (Dyn, Time, Time_init, dt_real) + +type(dynamics_type), intent(inout) :: Dyn +type(time_type) , intent(in) :: Time, Time_init +real, intent(in) :: dt_real + +integer :: i, j + +real, allocatable, dimension(:) :: glon_bnd, glat_bnd +complex, allocatable, dimension(:,:) :: div +real :: xx, yy, dd + +integer :: ierr, io, unit, pe +logical :: root + +! < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > + +call write_version_number (version, tagname) + +pe = mpp_pe() +root = (pe == mpp_root_pe()) + +if (file_exist('input.nml')) then + unit = open_namelist_file () + ierr=1 + do while (ierr /= 0) + read (unit, nml=barotropic_dynamics_nml, iostat=io, end=10) + ierr = check_nml_error (io, 'barotropic_dynamics_nml') + enddo + 10 call close_file (unit) +endif + + +if (root) write (stdlog(), nml=barotropic_dynamics_nml) + +call transforms_init(radius, num_lat, num_lon, num_fourier, fourier_inc, num_spherical, & + south_to_north=south_to_north, & + triang_trunc=triang_trunc, & + longitude_origin=longitude_origin ) + +call get_grid_domain(is,ie,js,je) +call get_spec_domain(ms,me,ns,ne) + +Dyn%num_lon = num_lon +Dyn%num_lat = num_lat +Dyn%spec_tracer = spec_tracer +Dyn%grid_tracer = grid_tracer + +allocate (sin_lat (js:je)) +allocate (cos_lat (js:je)) +allocate (deg_lat (js:je)) +allocate (deg_lon (is:ie)) +allocate (rad_lat (js:je)) +allocate (rad_lon (is:ie)) +allocate (coriolis (js:je)) + +allocate (glon_bnd (num_lon + 1)) +allocate (glat_bnd (num_lat + 1)) + +call get_deg_lon (deg_lon) +call get_deg_lat (deg_lat) +call get_sin_lat (sin_lat) +call get_cos_lat (cos_lat) +call get_grid_boundaries (glon_bnd, glat_bnd, global=.true.) + +coriolis = 2*omega*sin_lat + +rad_lat = deg_lat*atan(1.0)/45.0 +rad_lon = deg_lon*atan(1.0)/45.0 + +call spectral_damping_init(damping_coeff, damping_order, damping_option, cutoff_wn, num_fourier, num_spherical, 1, 0., 0., 0., & + damping_coeff_r=damping_coeff_r) + +allocate (Dyn%spec%vor (ms:me, ns:ne, num_time_levels)) +allocate (Dyn%grid%u (is:ie, js:je, num_time_levels)) +allocate (Dyn%grid%v (is:ie, js:je, num_time_levels)) +allocate (Dyn%grid%vor (is:ie, js:je, num_time_levels)) + +allocate (Dyn%tend%u (is:ie, js:je)) +allocate (Dyn%tend%v (is:ie, js:je)) +allocate (Dyn%grid%stream (is:ie, js:je)) +allocate (Dyn%grid%pv (is:ie, js:je)) +allocate (Dyn%grid%zonal_u_init(js:je)) + +allocate (div (ms:me, ns:ne)) + +call fv_advection_init(num_lon, num_lat, glat_bnd, 360./float(fourier_inc)) +if(Dyn%grid_tracer) then + allocate(Dyn%Grid%tr (is:ie, js:je, num_time_levels)) + allocate(Dyn%Tend%tr (is:ie, js:je)) +endif + +if(Dyn%spec_tracer) then + allocate(Dyn%Grid%trs (is:ie, js:je, num_time_levels)) + allocate(Dyn%Tend%trs (is:ie, js:je)) + allocate(Dyn%Spec%trs (ms:me, ns:ne, num_time_levels)) +endif + +if(trim(initial_zonal_wind) == 'zero') then + Dyn%grid%zonal_u_init = 0.0 +else if(trim(initial_zonal_wind) == 'two_jets') then + do j = js, je + Dyn%grid%zonal_u_init(j) = 25.0*cos_lat(j) & + - 30.0*(cos_lat(j)**3) & + + 300.0*(sin_lat(j)**2)*(cos_lat(j)**6) + enddo +else + call error_mesg('barotropic_dynamics_init',trim(initial_zonal_wind)// & + ' is not a valid value of initial_zonal_wind ', FATAL) +endif + +if(Time == Time_init) then + + do j = js, je + Dyn%Grid%u(:,j,1) = Dyn%grid%zonal_u_init(j) + Dyn%Grid%v(:,j,1) = 0.0 + end do + + call vor_div_from_uv_grid(Dyn%Grid%u (:,:,1), Dyn%Grid%v (:,:,1), & + Dyn%Spec%vor(:,:,1), div) + + call trans_spherical_to_grid(Dyn%Spec%vor(:,:,1), Dyn%Grid%vor(:,:,1)) + + do j = js, je + do i = is, ie + yy = (deg_lat(j)- eddy_lat)/eddy_width + Dyn%Grid%vor(i,j,1) = Dyn%Grid%vor(i,j,1) + & + 0.5*zeta_0*cos_lat(j)*exp(-yy*yy)*cos(m_0*rad_lon(i)) + end do + end do + + call trans_grid_to_spherical(Dyn%Grid%vor(:,:,1), Dyn%Spec%vor(:,:,1)) + + div = (0.,0.) + call uv_grid_from_vor_div (Dyn%Spec%vor(:,:,1), div, & + Dyn%Grid%u (:,:,1), Dyn%Grid%v (:,:,1)) + + if(Dyn%grid_tracer) then + Dyn%Grid%tr = 0.0 + do j = js, je + if(deg_lat(j) > 10.0 .and. deg_lat(j) < 20.0) Dyn%Grid%tr(:,j,1) = 1.0 + if(deg_lat(j) > 70.0 ) Dyn%Grid%tr(:,j,1) = -1.0 + end do + endif + + if(Dyn%spec_tracer) then + Dyn%Grid%trs = 0.0 + do j = js, je + if(deg_lat(j) > 10.0 .and. deg_lat(j) < 20.0) Dyn%Grid%trs(:,j,1) = 1.0 + if(deg_lat(j) > 70.0 ) Dyn%Grid%trs(:,j,1) = -1.0 + end do + call trans_grid_to_spherical(Dyn%Grid%trs(:,:,1), Dyn%Spec%trs(:,:,1)) + endif + +else + + call read_restart(Dyn) + +endif + +module_is_initialized = .true. + +return +end subroutine barotropic_dynamics_init + +!=============================================================================================== + +subroutine barotropic_dynamics(Time, Time_init, Dyn, previous, current, future, delta_t) + +type(time_type) , intent(in) :: Time, Time_init +type(dynamics_type), intent(inout) :: Dyn +integer, intent(in ) :: previous, current, future +real, intent(in ) :: delta_t + +! < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > + +complex, dimension(ms:me, ns:ne) :: dt_vors, dt_divs, stream, zeros, spec_diss +real, dimension(is:ie, js:je) :: dt_vorg +integer :: j, seconds, days + +! < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > + +if(.not.module_is_initialized) then + call error_mesg('barotropic_dynamics','dynamics has not been initialized ', FATAL) +endif + +zeros = (0.,0.) + +do j = js, je + Dyn%grid%pv(:,j) = Dyn%grid%vor(:,j,current) + coriolis(j) +end do + +Dyn%Tend%u = Dyn%Tend%u + Dyn%grid%pv*Dyn%Grid%v(:,:,current) +Dyn%Tend%v = Dyn%Tend%v - Dyn%grid%pv*Dyn%Grid%u(:,:,current) + +call vor_div_from_uv_grid (Dyn%Tend%u, Dyn%Tend%v, dt_vors, dt_divs) + +call compute_spectral_damping(Dyn%Spec%vor(:,:,previous), dt_vors, delta_t) + +call stirring(Time, dt_vors) + +call leapfrog(Dyn%Spec%vor , dt_vors , previous, current, future, delta_t, robert_coeff, raw_filter_coeff) + +call trans_spherical_to_grid(Dyn%Spec%vor(:,:,future), Dyn%Grid%vor(:,:,future)) + +call uv_grid_from_vor_div (Dyn%Spec%vor (:,:,future), zeros, & + Dyn%Grid%u (:,:,future), Dyn%Grid%v (:,:,future)) + +if(minval(Dyn%Grid%v) < valid_range_v(1) .or. maxval(Dyn%Grid%v) > valid_range_v(2)) then + call get_time (Time, seconds, days) + call mpp_error(FATAL,'barotropic_dynamics: Meridional wind out of valid range. Model time=',days,' days ',seconds,' seconds') +endif + +if(Dyn%spec_tracer) call update_spec_tracer(Dyn%Spec%trs, Dyn%Grid%trs, Dyn%Tend%trs, & + Dyn%Grid%u, Dyn%Grid%v, previous, current, future, delta_t) + +if(Dyn%grid_tracer) call update_grid_tracer(Dyn%Grid%tr, Dyn%Tend%tr, & + Dyn%Grid%u, Dyn%Grid%v, previous, current, future, delta_t) + +stream = compute_laplacian(Dyn%Spec%vor(:,:,current), -1) +call trans_spherical_to_grid(stream, Dyn%grid%stream) + +return +end subroutine barotropic_dynamics + +!=================================================================================== + +subroutine update_spec_tracer(tr_spec, tr_grid, dt_tr, ug, vg, & + previous, current, future, delta_t) + +complex, intent(inout), dimension(ms:me, ns:ne, num_time_levels) :: tr_spec +real , intent(inout), dimension(is:ie, js:je, num_time_levels) :: tr_grid +real , intent(inout), dimension(is:ie, js:je ) :: dt_tr +real , intent(in ), dimension(is:ie, js:je, num_time_levels) :: ug, vg +real , intent(in ) :: delta_t +integer, intent(in ) :: previous, current, future + +complex, dimension(ms:me, ns:ne) :: dt_trs + +call horizontal_advection (tr_spec(:,:,current), ug(:,:,current), vg(:,:,current), dt_tr) +call trans_grid_to_spherical (dt_tr, dt_trs) +call compute_spectral_damping (tr_spec(:,:,previous), dt_trs, delta_t) +call leapfrog (tr_spec, dt_trs, previous, current, future, delta_t, robert_coeff, raw_filter_coeff) +call trans_spherical_to_grid (tr_spec(:,:,future), tr_grid(:,:,future)) + +return +end subroutine update_spec_tracer +!========================================================================== + +subroutine update_grid_tracer(tr_grid, dt_tr_grid, ug, vg, & + previous, current, future, delta_t) + +real , intent(inout), dimension(is:ie, js:je, num_time_levels) :: tr_grid +real , intent(inout), dimension(is:ie, js:je ) :: dt_tr_grid +real , intent(in ), dimension(is:ie, js:je, num_time_levels) :: ug, vg + +real , intent(in ) :: delta_t +integer, intent(in ) :: previous, current, future + +real, dimension(size(tr_grid,1),size(tr_grid,2)) :: tr_current, tr_future + +tr_future = tr_grid(:,:,previous) + delta_t*dt_tr_grid +dt_tr_grid = 0.0 +call a_grid_horiz_advection (ug(:,:,current), vg(:,:,current), tr_future, delta_t, dt_tr_grid) +tr_future = tr_future + delta_t*dt_tr_grid +tr_current = tr_grid(:,:,current) + & + robert_coeff*(tr_grid(:,:,previous) + tr_future - 2.0*tr_grid(:,:,current)) +tr_grid(:,:,current) = tr_current +tr_grid(:,:,future) = tr_future + +return +end subroutine update_grid_tracer + +!========================================================================== + +subroutine read_restart(Dyn) + +type(dynamics_type), intent(inout) :: Dyn + +integer :: unit, m, n, nt +real, dimension(ms:me, ns:ne) :: real_part, imag_part + +if(file_exist('INPUT/barotropic_dynamics.res.nc')) then + do nt = 1, 2 + call read_data('INPUT/barotropic_dynamics.res.nc', 'vors_real', real_part, spectral_domain, timelevel=nt) + call read_data('INPUT/barotropic_dynamics.res.nc', 'vors_imag', imag_part, spectral_domain, timelevel=nt) + do n=ns,ne + do m=ms,me + Dyn%Spec%vor(m,n,nt) = cmplx(real_part(m,n),imag_part(m,n)) + end do + end do + if(Dyn%spec_tracer) then + call read_data('INPUT/barotropic_dynamics.res.nc', 'trs_real', real_part, spectral_domain, timelevel=nt) + call read_data('INPUT/barotropic_dynamics.res.nc', 'trs_imag', imag_part, spectral_domain, timelevel=nt) + do n=ns,ne + do m=ms,me + Dyn%Spec%trs(m,n,nt) = cmplx(real_part(m,n),imag_part(m,n)) + end do + end do + endif + call read_data('INPUT/barotropic_dynamics.res.nc', 'u', Dyn%Grid%u (:,:,nt), grid_domain, timelevel=nt) + call read_data('INPUT/barotropic_dynamics.res.nc', 'v', Dyn%Grid%v (:,:,nt), grid_domain, timelevel=nt) + call read_data('INPUT/barotropic_dynamics.res.nc', 'vor', Dyn%Grid%vor(:,:,nt), grid_domain, timelevel=nt) + if(Dyn%spec_tracer) then + call read_data('INPUT/barotropic_dynamics.res.nc', 'trs', Dyn%Grid%trs(:,:,nt), grid_domain, timelevel=nt) + endif + if(Dyn%grid_tracer) then + call read_data('INPUT/barotropic_dynamics.res.nc', 'tr', Dyn%Grid%tr(:,:,nt), grid_domain, timelevel=nt) + endif + end do +else if(file_exist('INPUT/barotropic_dynamics.res')) then + unit = open_restart_file(file='INPUT/barotropic_dynamics.res',action='read') + + do nt = 1, 2 + call set_domain(spectral_domain) + call read_data(unit,Dyn%Spec%vor(:,:, nt)) + if(Dyn%spec_tracer) call read_data(unit,Dyn%Spec%trs(:,:, nt)) + + call set_domain(grid_domain) + call read_data(unit,Dyn%Grid%u (:,:, nt)) + call read_data(unit,Dyn%Grid%v (:,:, nt)) + call read_data(unit,Dyn%Grid%vor (:,:, nt)) + if(Dyn%spec_tracer) call read_data(unit,Dyn%Grid%trs(:,:, nt)) + if(Dyn%grid_tracer) call read_data(unit,Dyn%Grid%tr (:,:, nt)) + + end do + call close_file(unit) + +else + call error_mesg('read_restart', 'restart does not exist', FATAL) +endif + +return +end subroutine read_restart + +!==================================================================== + +subroutine write_restart(Dyn, previous, current) + +type(dynamics_type), intent(in) :: Dyn +integer, intent(in) :: previous, current + +integer :: unit, nt, nn + +do nt = 1, 2 + if(nt == 1) nn = previous + if(nt == 2) nn = current + call write_data('RESTART/barotropic_dynamics.res.nc', 'vors_real', real(Dyn%Spec%vor(:,:,nn)), spectral_domain) + call write_data('RESTART/barotropic_dynamics.res.nc', 'vors_imag', aimag(Dyn%Spec%vor(:,:,nn)), spectral_domain) + if(Dyn%spec_tracer) then + call write_data('RESTART/barotropic_dynamics.res.nc', 'trs_real', real(Dyn%Spec%trs(:,:,nn)), spectral_domain) + call write_data('RESTART/barotropic_dynamics.res.nc', 'trs_imag', aimag(Dyn%Spec%trs(:,:,nn)), spectral_domain) + endif + call write_data('RESTART/barotropic_dynamics.res.nc', 'u', Dyn%Grid%u (:,:,nn), grid_domain) + call write_data('RESTART/barotropic_dynamics.res.nc', 'v', Dyn%Grid%v (:,:,nn), grid_domain) + call write_data('RESTART/barotropic_dynamics.res.nc', 'vor', Dyn%Grid%vor(:,:,nn), grid_domain) + if(Dyn%spec_tracer) then + call write_data('RESTART/barotropic_dynamics.res.nc', 'trs', Dyn%Grid%trs(:,:,nn), grid_domain) + endif + if(Dyn%grid_tracer) then + call write_data('RESTART/barotropic_dynamics.res.nc', 'tr', Dyn%Grid%tr(:,:,nn), grid_domain) + endif +enddo + +!unit = open_restart_file(file='RESTART/barotropic_dynamics.res', action='write') + +!do nt = 1, 2 +! if(nt == 1) nn = previous +! if(nt == 2) nn = current + +! call set_domain(spectral_domain) +! call write_data(unit,Dyn%Spec%vor(:,:, nn)) +! if(Dyn%spec_tracer) call write_data(unit,Dyn%Spec%trs(:,:, nn)) + +! call set_domain(grid_domain) +! call write_data(unit,Dyn%Grid%u (:,:, nn)) +! call write_data(unit,Dyn%Grid%v (:,:, nn)) +! call write_data(unit,Dyn%Grid%vor (:,:, nn)) +! if(Dyn%spec_tracer) call write_data(unit,Dyn%Grid%trs(:,:, nn)) +! if(Dyn%grid_tracer) call write_data(unit,Dyn%Grid%tr (:,:, nn)) +!end do + +!call close_file(unit) + +end subroutine write_restart + +!==================================================================== + +subroutine barotropic_dynamics_end (Dyn, previous, current) + +type(dynamics_type), intent(inout) :: Dyn +integer, intent(in) :: previous, current + +if(.not.module_is_initialized) then + call error_mesg('barotropic_dynamics','dynamics has not been initialized ', FATAL) +endif + +call transforms_end() +call stirring_end() + +call write_restart (Dyn, previous, current) + +module_is_initialized = .false. + +return +end subroutine barotropic_dynamics_end +!=================================================================================== + +end module barotropic_dynamics_mod diff --git a/src/atmos_spectral_barotropic/barotropic_dynamics.html b/src/atmos_spectral_barotropic/barotropic_dynamics.html new file mode 100644 index 000000000..62c97898a --- /dev/null +++ b/src/atmos_spectral_barotropic/barotropic_dynamics.html @@ -0,0 +1,371 @@ + +module barotropic_dynamics_mod + + +
+ + +

module barotropic_dynamics_mod

+ + +
+     Contact: Isaac Held
+     Reviewers: Peter Phillipps
+
+
+
+ + +
+

OVERVIEW

+ +
+
+   The dynamical core of the spectral transform model for 
+   two-dimensional, non-divergent flow on the surface of the sphere.  
+   
+
+
+ + +
+

DESCRIPTION

+ +
+
+   Integrates the barotropic vorticity equation for nondivergent flow on the
+   sphere using the spectral transform technique.  Also allows for the
+   inclusion of a passive tracer advected by the same spectral advection
+   algorithm as  the vorticity, and a gridpoint tracer advected with a finite
+   volume  algorithm on the transform grid.  The default initial condition 
+   provided as an example is a zonal flow resembling that in the Northern
+   winter,  plus a sinusoidal disurbance localized in midlatitudes.
+
+   For a full description of the model and algorithms used, see 
+     barotropic.ps 
+   
+   For higher level routines for running this barotropic spectral model,
+   see  atmosphere_mod 
+
+
+
+ + + +
+

OTHER MODULES USED

+ +
+
+     fms_mod
+     constants_mod
+     time_manager_mod
+     transforms_mod
+     spectral_damping_mod
+     leapfrog_mod
+     fv_advection_mod
+
+
+
+ + +
+

PUBLIC INTERFACE

+ +
+
+  use barotropic_dynamics_mod [,only: barotropic_dynamics_init,       
+                                      barotropic_dynamics,
+			              barotropic_dynamics_end,
+                                      dynamics_type,
+				      grid_type,
+				      spectral_type,
+				      tendency_type]
+                                
+
+
+ + +
+

PUBLIC DATA

+ +
+     
+
+
+ +type grid_type + real, pointer, dimension(:,:,:) :: u, v, vor, trs, tr, pv + real, pointer, dimension(:,:) :: stream +end type + + allocated space for grid fields + + (:,:,:) => (lon, lat, time_level) + (:,:) => (lon, lat) + (lon, lat) on local computational domain + time_level stores the two time levels needed for the + leapfrog step + + u -- eastward velocity (m/s) + v -- northward velocity (m/s) + vor -- vorticity (1/s) + trs -- tracer advected spectrally + tr -- tracer advected on grid + pv -- absolute vorticity, f + vor, where f = 2*omega*sin(lat) (1/s) + stream -- streamfunction (m^2/s) at current time + + + +
+ + +type spectral_type + complex, pointer, dimension(:,:,:) :: vor, trs +end type + + allocated space for spectral fields + + (:,:,:) => (zonal, meridional, time_level) + + vor -- spectral vorticity + trs -- spectral tracer + +
+
+ +type tendency_type + real, pointer, dimension(:,:) :: u, v, trs, tr +end type + + allocated space for accumulating tendencies, d/dt, in grid space, + for prognostic variables + + (:,:,:) => (lon, lat) + +
+
+ +type dynamics_type + type(grid_type) :: grid + type(spectral_type) :: spec + type(tendency_type) :: tend + integer :: num_lon, num_lat ! size of global domain + logical :: grid_tracer, spec_tracer +end type + + grid_tracer = .true. => tracer with gridpoint advection is beign integrated + similarly for spec_tracer + +
+ +
+ + + +
+

PUBLIC ROUTINES

+ +
+
+subroutine  barotropic_dynamics_init
+subroutine  barotropic _dynamics
+subroutine  barotropic_dynamics_end
+type (grid_type)
+type (spectral_type)
+type (tendency_type)
+type (dynamics_type)
+
+
+
+
+
+ subroutine barotropic_dynamics_init(Dyn,  Time, Time_init)
+ 
+   type(dynamics_type), intent(inout)  :: Dyn
+         type containing all dynamical fields and related information
+	 (see type (dynamics_type))
+	 
+   type(time_type)    , intent(in)     :: Time, Time_init
+         current time and time at which integeration began
+	 time_type defined by time_manager_mod
+         
+
+   Initializes the module;
+   Reads restart from 'INPUT/barotropic_dynamics.res' if Time = Time_init;
+     otherwise uses default initial conditions
+
+
+
+
+ + + + subroutine barotropic_dynamics & + (Time, Time_init, Dyn, previous, current, future, delta_t) + + type(time_type) , intent(inout) :: Time, Time_init + type(dynamics_type), intent(inout) :: Dyn + integer , intent(in ) :: previous, current, future + real , intent(in ) :: delta_t + + previous, current and future = 1 or 2 + these integers refer to the third dimension of the + three-dimensional fields in Dyn + the fields at time t - delta_t are assumed to be in (:,:,previous) + the fields at time t are assumed to be in (:,:,current) + the fields at time t + delta_t are placed in (:,:,future) + overwriting whatever is already there + + delta_t = time step in seconds + + updates dynamical fields by one time step + + + +
+ + + subroutine barotropic_dynamics_end(Dyn, previous, current) + + type(dynamics_type), intent(inout) :: Dyn + integer, intent(in) :: previous, current + + + Terminates module; + writes restart file to 'RESTART/barotropic_dynamics.res' + + + + +
+
+ + + +
+

NAMELIST

+ +
+
+&barotropic_dynamics_nml
+
+  integer :: num_lat            = 128  
+        number of latitudes in global grid
+       
+  integer :: num_lon            = 256
+        number of longitudes in global grid
+        should equal 2*num_lat for Triangular truncation
+  
+  integer :: num_fourier        = 85
+        the retained fourier wavenumber are n*fourier_inc, where
+        n ranges from 0 to num_fourier
+	 
+  integer :: num_spherical      = 86
+        the maximum number of meridional modes for any zonal wavenumber
+        for triangular truncation, set num_spherical = num_fourier +1
+         
+  integer :: fourier_inc        = 1
+        creates a "sector" model if fourier_inc > 1; integration domain is
+	(360 degrees longitude)/fourier_inc
+	
+  (the default values listed above define a standard T85 model)
+
+  logical :: check_fourier_imag = .false.
+        if true, checks to see if fields to be transformed to grid 
+	domain have zero imaginary part to their zonally symmetric
+	modes; useful for debugging
+	
+  logical :: south_to_north     = .true.
+        true => grid runs from south to north
+	false => grid runs from north to south
+	
+  logical :: triangular_trunc   = .true.
+        true  => shape of truncation is triangular
+	false => shape of truncation is rhomboidal
+
+  real    :: robert_coeff       = 0.04
+        x(current) => (1-2r)*x(current) + r*(x(future)+x(previous))
+	where r = robert_coeff (non-dimensional)
+	
+  real    :: longitude_origin   = 0.0
+        longitude of first longitude, in degrees
+	(if you want the westgern boundary of first grid boc to be at 
+         0.0, set longitude_origin = 0.5*360./float(num_lon))
+	 
+  integer :: damping_option     = 'resolution_dependent'
+  integer :: damping_order      = 4
+  real    :: damping_coeff      = 1.e-04
+  
+        damping = nu*(del^2)^n where n = damping order
+	damping_option = 'resolution_dependent' or 'resolution_independent'
+	  = 'resolution_dependent' => nu is set so that the damping rate for the 
+	        mode (m=0,n=num_spherical-1) equals damping_coeff (in 1/s)
+	        For triangular truncation, damping_coeff is then the 
+	        rate of damping of the highest retained mode
+	     
+	  = 'resolution_independent' => nu = damping_coef
+	
+	
+  real     :: zeta_0     = 8.e-05
+  integer  :: m_0        = 4
+  real     :: eddy_width = 15.0
+  real     :: eddy_lat   = 45.0
+  
+         eddy component of the initial condition is sinusoidal with
+	 wavenumber m_0 and with a gaussian distribution of 
+	 vorticity in latitude, centered at eddy_lat with half-width
+	 eddy_width
+	 
+	 zeta_0 ( 1/s)
+	 eddy_width and eddy_lat (degrees)
+
+  logical :: spec_tracer      = .true.
+  logical :: grid_tracer      = .true.
+       spec_tracer = true => a passive tracer is carried that is advected
+          spectrally, with the same algorithm as the vorticity
+       grid_tracer = ture => a passive tracer is carried that is advected
+          on the spectral transform grid by a finite-volume algorithm
+	  (see  barotropic.ps )
+       Both tracers can be carried simultaeneously
+	  
+The vorticity and the tracers are initialized within subroutine
+     barotropic_dynamics_init
+
+  real, dimension(2) :: valid_range_v = -1000., 1000.
+        A valid range for meridional wind. Model terminates if meridional wind
+        goes outside the valid range. Allows model to terminate gracefully when,
+        for example, the model becomes numerically unstable.
+
+
+ + + +
+

ERROR MESSAGES

+ +
+
+   "Dynamics has not been initialized"
+      -- barotropic_dynamics_init must be called before any other
+         routines in the module are called
+	 
+   "restart does not exist" 
+      -- Time is not equal to Time_init at initalization, but the file
+          'INPUT/barotropic_dynamics.res' does not exit 
+	 
+
+
+
+ + +
+ + diff --git a/src/atmos_spectral_barotropic/barotropic_physics.F90 b/src/atmos_spectral_barotropic/barotropic_physics.F90 new file mode 100644 index 000000000..8590bacce --- /dev/null +++ b/src/atmos_spectral_barotropic/barotropic_physics.F90 @@ -0,0 +1,173 @@ +module barotropic_physics_mod + +!----------------------------------------------------------------------- +! GNU General Public License +! +! This program is free software; you can redistribute it and/or modify it and +! are expected to follow the terms of the GNU General Public License +! as published by the Free Software Foundation; either version 2 of +! the License, or (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, but WITHOUT +! ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +! or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +! License for more details. +! +! For the full text of the GNU General Public License, +! write to: Free Software Foundation, Inc., +! 675 Mass Ave, Cambridge, MA 02139, USA. +! or see: http://www.gnu.org/licenses/gpl.html +!----------------------------------------------------------------------- + +use fms_mod, only: open_namelist_file, & + open_restart_file, & + file_exist, & + check_nml_error, & + error_mesg, & + FATAL, WARNING, & + write_version_number, & + mpp_pe, & + mpp_root_pe, & + fms_init, fms_end, & + read_data, & + write_data, & + set_domain, & + close_file, & + stdlog + +use transforms_mod, only: get_sin_lat, get_cos_lat, & + get_deg_lon, get_deg_lat, & + get_wts_lat, & + get_grid_domain, get_spec_domain, & + grid_domain + +use time_manager_mod, only: time_type + +!======================================================================== +implicit none +private +!======================================================================== + +public :: barotropic_physics_init, & + barotropic_physics, & + barotropic_physics_end, & + phys_type + +! version information +!======================================================================== +character(len=128) :: version = '$Id: barotropic_physics.F90,v 10.0 2003/10/24 22:00:58 fms Exp $' +character(len=128) :: tagname = '$Name: siena_201207 $' +!======================================================================== + +type phys_type + real, pointer, dimension(:,:) :: empty=>NULL() +end type + +logical :: module_is_initialized = .false. + +integer :: is, ie, js, je + + +integer :: pe +logical :: root + +real, allocatable, dimension(:) :: rad_lat, & + deg_lat, & + sin_lat, & + cos_lat, & + wts_lat + +! namelist +!======================================================================== + +logical :: empty + +namelist /barotropic_physics_nml/ empty +!======================================================================== + +contains + +!======================================================================== + +subroutine barotropic_physics_init(Phys) + +type(phys_type), intent(inout) :: Phys + +integer :: j, unit, ierr, io + +call write_version_number (version, tagname) + +pe = mpp_pe() +root = (pe == mpp_root_pe()) + +! read the namelist + +if (file_exist('input.nml')) then + unit = open_namelist_file () + ierr=1 + do while (ierr /= 0) + read (unit, nml=barotropic_physics_nml, iostat=io, end=10) + ierr = check_nml_error (io, 'barotropic_physics_nml') + enddo + 10 call close_file (unit) +endif + +call get_grid_domain(is,ie,js,je) + +allocate ( rad_lat (js:je) ) +allocate ( deg_lat (js:je) ) +allocate ( sin_lat (js:je) ) +allocate ( cos_lat (js:je) ) +allocate ( wts_lat (js:je) ) + +call get_wts_lat(wts_lat) +call get_deg_lat(deg_lat) +rad_lat = deg_lat*atan(1.)/45. +sin_lat = sin(rad_lat) +cos_lat = cos(rad_lat) + +module_is_initialized = .true. + +return +end subroutine barotropic_physics_init + +!======================================================================= + +subroutine barotropic_physics(Time, dt_ug, dt_vg, ug, vg, & + delta_t, previous, current, Phys) + +real, intent(inout), dimension(is:ie, js:je) :: dt_ug, dt_vg +real, intent(in) , dimension(is:ie, js:je, 2) :: ug, vg + +real , intent(in) :: delta_t +integer, intent(in) :: previous, current + +type(time_type), intent(in) :: Time +type(phys_type), intent(inout) :: Phys + +if(.not.module_is_initialized) call error_mesg('barotropic_physics', & + 'barotropic_physics is not initialized', FATAL) + +! dt_ug = dt_ug +f(ug,vg) +! dt_vg = dt_vg +f(ug,vg) +! Phys%empty = + +return +end subroutine barotropic_physics + +!====================================================================== + +subroutine barotropic_physics_end(Phys) + +type(phys_type), intent(in) :: Phys + +if(.not.module_is_initialized) call error_mesg('barotropic_physics_end', & + 'barotropic_physics is not initialized', FATAL) + +module_is_initialized = .false. +return +end subroutine barotropic_physics_end + +!====================================================================== + +end module barotropic_physics_mod diff --git a/src/atmos_spectral_barotropic/barotropic_physics.html b/src/atmos_spectral_barotropic/barotropic_physics.html new file mode 100644 index 000000000..e391ca50f --- /dev/null +++ b/src/atmos_spectral_barotropic/barotropic_physics.html @@ -0,0 +1,165 @@ + +module barotropic_physics_mod + + +
+ + +

module barotropic_physics_mod

+ + +
+     Contact: Isaac Held
+     Reviewers: Peter Phillipps
+
+
+
+ + +
+

OVERVIEW

+ +
+
+   A module that allows one to add processes that act in the grid domain
+   to the dynamics of the barotropic model on the sphere
+   
+
+
+ + +
+

DESCRIPTION

+ +
+
+   A module that allows one to add processes that act in the grid domain
+   to the dynamics of the barotropic model on the sphere.  Currently,
+   does nothing!
+
+
+
+ + +
+

OTHER MODULES USED

+ +
+
+     fms_mod
+     transforms_mod
+     time_manager_mod
+
+
+
+ + +
+

PUBLIC INTERFACE

+ +
+
+  use barotropic_physics_mod [,only: barotropic_physics_init,       
+                                         barotropic_physics,
+					 barotropic_physics_end,
+					 phys_type]
+                                
+
+
+ + +
+

PUBLIC DATA

+ +
+     
+
+
+ +type phys_type + real, pointer, dimension(:,:) :: empty +end type + + fields from physics module made available for diagnostics + +
+
+ +
+ + +
+

PUBLIC ROUTINES

+ +
+
+subroutine  barotropic_physics_init
+subroutine  barotropic_physics
+subroutine  barotropic_physics_end
+type(phys_type)
+
+
+
+
+
+ subroutine barotropic_physics_init(Phys)
+ 
+   type(phys_type)    , intent(inout)     :: Phys
+  
+ 
+   Initializes module
+
+
+
+
+ + + + subroutine barotropic_physics (Time, dt_ug, dt_vg, ug, vg, & + delta_t, previous, current, Phys) + + real, intent(inout), dimension(:,:) :: dt_ug, dt_vg + + the u and v tendencies onto which tendencies due to + the grid-point physics are added (m/(s^2)) + + real, intent(in) , dimension(:,:, 2) :: ug, vg + the grid zonal and meridional velocities (m/s) + the third index is the time-index used in the leapfrog step + + real , intent(in) :: delta_t + time step (s) + + integer, intent(in) :: previous, current + = 1 or 2 + ug(:,:,previous) is the velocity at t-delta_t + ug(:,:,current ) is the velocity at t + + type(time_type), intent(in) :: Time + type(phys_type), intent(inout) :: Phys + + + + +
+ + + + subroutine barotropic_physics_end (Phys) + + type(phys_type), intent(inout) :: Phys + + + + +
+ + + diff --git a/src/atmos_spectral_barotropic/stirring.F90 b/src/atmos_spectral_barotropic/stirring.F90 new file mode 100644 index 000000000..7de10c3f1 --- /dev/null +++ b/src/atmos_spectral_barotropic/stirring.F90 @@ -0,0 +1,252 @@ +module stirring_mod + +!----------------------------------------------------------------------- +! GNU General Public License +! +! This program is free software; you can redistribute it and/or modify it and +! are expected to follow the terms of the GNU General Public License +! as published by the Free Software Foundation; either version 2 of +! the License, or (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, but WITHOUT +! ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +! or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +! License for more details. +! +! For the full text of the GNU General Public License, +! write to: Free Software Foundation, Inc., +! 675 Mass Ave, Cambridge, MA 02139, USA. +! or see: http://www.gnu.org/licenses/gpl.html +!----------------------------------------------------------------------- + +! Stirring is computed as described in the following paper: + +! Vallis, Gerber, Kushner, Cash, 2003: A Mechanism and Simple Dynamical Model of the North Atlantic Oscillation and Annular Modes. +! J. Atmos. Sci., 61, 264-280. + +! Stirring is not part of barotropic_physics because barotropic_physics appears to be intended for +! operations that are done completely in grid space. Stirring is computed partly in spectral space. + +use constants_mod, only: pi + +use time_manager_mod, only: time_type + +use fms_mod, only: open_namelist_file, check_nml_error, close_file, write_version_number, & + stdlog, mpp_pe, mpp_root_pe, file_exist, read_data, write_data, error_mesg, FATAL + +use transforms_mod, only: get_spec_domain, get_grid_domain, trans_spherical_to_grid, trans_grid_to_spherical, & + grid_domain, get_lon_max, get_lat_max, get_deg_lon, get_deg_lat, get_grid_boundaries, & + get_num_fourier, get_num_spherical, spectral_domain + +use diag_manager_mod, only: diag_axis_init, register_static_field, register_diag_field, send_data + +implicit none +private + +integer :: ms,me,ns,ne,is,ie,js,je +integer :: id_str_amp, id_g_stir_sqr, id_stir +logical :: used +logical, allocatable, dimension(:,:) :: wave_mask ! wave_mask(m,n) = .true. if spherical wave (m,n) is to be excited +complex, allocatable, dimension(:,:) :: s_stir ! stirring. Saved from one time step to the next +real, allocatable, dimension(:,:) :: localize ! localizes the stirring +real, allocatable, dimension(:,:) :: g_stir_sqr ! time mean of g_stir**2 over entire integration +integer, allocatable, dimension(:) :: seed ! random number seed +real :: astir, bstir +integer :: num_steps, num_fourier, num_spherical, nseed + +logical :: module_is_initialized = .false. + +character(len=128) :: version = '$Id: stirring.F90,v 17.0 2009/07/21 03:00:25 fms Exp $' +character(len=128) :: tagname = '$Name: siena_201207 $' + +public :: stirring_init, stirring, stirring_end + +real :: decay_time=2*86400, amplitude=0.0, lat0=45., widthy=12. +logical :: do_localize=.true.!Default true to allow forcing to be localized in physical space. Set to false to have forcing everywhere. + +! Set B to a non-zero value for stirring that has zonal structure. +! The strength of the stirring at latitude=lat0 is: amplitude*(1.0 + B*exp(-.5*((lon-lon0)/widthx)**2)) +real :: lon0=180., B=0.0, widthx=45., C=1.0 ! widthx +integer :: n_total_forcing_max = 15 !total wavenumbers LESS THAN this number will be forced +integer :: n_total_forcing_min = 9 !total wavenumbers GREATER THAN this number will be forced +integer :: zonal_forcing_min = 3 !Zonal wavenumbers GREATER THAN this number will be forced, subject to total wavenumber constraints + +namelist / stirring_nml / decay_time, amplitude, lat0, lon0, widthy, widthx, B, do_localize, n_total_forcing_max, n_total_forcing_min, zonal_forcing_min + +contains + +!================================================================================================================================ +subroutine stirring_init(dt, Time, id_lon, id_lat, id_lonb, id_latb) +real, intent(in) :: dt +type(time_type), intent(in) :: Time +integer, intent(in) :: id_lon, id_lat, id_lonb, id_latb +real :: xx, kk, rad_to_deg +integer :: i,j,m,n,ierr,io,unit,lon_max,lat_max +real, allocatable, dimension(:) :: ampx, ampy, lon, lat, lonb, latb +real, allocatable, dimension(:,:) :: real_part, imag_part + +if(module_is_initialized) return + +call write_version_number (version, tagname) + +if (file_exist('input.nml')) then + unit = open_namelist_file () + ierr=1 + do while (ierr /= 0) + read (unit, nml=stirring_nml, iostat=io, end=10) + ierr = check_nml_error (io, 'stirring_nml') + enddo + 10 call close_file (unit) +endif +if(mpp_pe() == mpp_root_pe()) write(stdlog(), nml=stirring_nml) + +call get_lon_max(lon_max) +call get_lat_max(lat_max) + +allocate(lon (lon_max )) ; lon = 0.0 +allocate(lat (lat_max )) ; lat = 0.0 +allocate(lonb(lon_max+1)) ; lonb = 0.0 +allocate(latb(lat_max+1)) ; latb = 0.0 + +call get_deg_lon(lon) +call get_deg_lat(lat) + +module_is_initialized = .true. +if(amplitude == 0.0) return ! stirring does nothing more unless amplitude is non-zero + +call get_spec_domain(ms,me,ns,ne) +call get_grid_domain(is,ie,js,je) +call get_num_fourier(num_fourier) +call get_num_spherical(num_spherical) + +allocate(wave_mask(ms:me,ns:ne)); wave_mask = .false. +allocate(s_stir(ms:me,ns:ne)); s_stir = cmplx(0.0,0.0) +allocate(ampx(is:ie)); ampx = 0.0 +allocate(ampy(js:je)); ampy = 0.0 +allocate(localize(is:ie,js:je)); localize = 0.0 +allocate(g_stir_sqr(is:ie,js:je)); g_stir_sqr = 0.0 + +! wave_mask is .true. when (m+n > 9) .and. (m+n < 15) .and. (m > 3) +do m=(zonal_forcing_min+1),(n_total_forcing_max-1) + if(m >= ms .and. m <= me) then + do n=(n_total_forcing_min+1)-m,(n_total_forcing_max-1)-m + if(n >= ns .and. n <= ne) then + wave_mask(m,n) = .true. + endif + enddo + endif +enddo + +astir = sqrt(1.0 - exp(-2*dt/decay_time)) +bstir = exp(-dt/decay_time) + +do i=is,ie + xx = lon(i)-lon0 + ! Make sure xx falls in the range -180. to +180. + kk = nint(xx/360.) + xx = xx - 360.*kk + ampx(i) = (1 + B*exp(-.5*(xx/widthx)**2)) +enddo +do j=js,je + ampy(j) = exp(-.5*((lat(j)-lat0)/widthy)**2) +enddo +if (do_localize) then + do j=js,je + do i=is,ie + localize(i,j) = ampx(i)*ampy(j) + enddo + enddo +else + localize = 1.0 +endif + +deallocate(ampx, ampy) + +num_steps = 0 +id_g_stir_sqr = register_static_field('stirring_mod', 'stirring_sqr', (/id_lon,id_lat/), 'stirring sqrared', '1/sec^4') +id_str_amp = register_static_field('stirring_mod', 'stirring_amp', (/id_lon,id_lat/), 'amplitude of stirring', 'none') +id_stir = register_diag_field ('stirring_mod', 'stirring', (/id_lon,id_lat/), Time, 'stirring', '1/sec^2') +used = send_data(id_str_amp, amplitude*localize) + +call random_seed(size=nseed) +allocate(seed(nseed)) + +if(file_exist('INPUT/stirring.res.nc')) then + allocate(real_part(ms:me,ns:ne), imag_part(ms:me,ns:ne)) + call read_data('INPUT/stirring.res.nc', 'stir_real', real_part, spectral_domain) + call read_data('INPUT/stirring.res.nc', 'stir_imag', imag_part, spectral_domain) + do n=ns,ne + do m=ms,me + s_stir(m,n) = cmplx(real_part(m,n),imag_part(m,n)) + end do + end do + deallocate(real_part, imag_part) + call read_data('INPUT/stirring.res.nc', 'ran_nmbr_seed', seed, no_domain=.true.) + call random_seed(put=seed) +endif + +end subroutine stirring_init +!================================================================================================================================ +subroutine stirring(Time, dt_vors) +type(time_type), intent(in) :: Time +complex, dimension(ms:me,ns:ne), intent(inout) :: dt_vors +real, dimension(is:ie,js:je) :: g_stir +complex, dimension(ms:me,ns:ne) :: new_stirring +real, dimension(0:num_fourier,0:num_spherical,2) :: ran_nmbrs +integer :: i,j,m,n +real :: x,y + +if(.not.module_is_initialized) then + call error_mesg('stirring', 'stirring_init has not been called', FATAL) +end if + +if(amplitude == 0.0) return ! stirring does nothing unless amplitude is non-zero + +call random_number(ran_nmbrs) + +do n=ns,ne +do m=ms,me + if(wave_mask(m,n)) then + new_stirring(m,n) = amplitude*astir*cmplx(2*ran_nmbrs(m,n,1)-1, 2*ran_nmbrs(m,n,2)-1) + else + new_stirring(m,n) = cmplx(0.0,0.0) + endif +enddo +enddo +call trans_spherical_to_grid(new_stirring,g_stir) +g_stir = localize*g_stir +call trans_grid_to_spherical(g_stir,new_stirring) +if(ms == 0 .and. ns == 0) then + new_stirring(0,0)=cmplx(0.0,0.0) ! A non-zero global mean is introduced by the grid space computation, but we don't want it. +endif +s_stir = bstir*s_stir + new_stirring !This is equation A.6 in Vallis et al 2004 - DOI:10.1175/1520-0469(2004)061<0264:AMASDM>2.0.CO;2 + +dt_vors = dt_vors + s_stir +call trans_spherical_to_grid(s_stir,g_stir) +g_stir_sqr = g_stir_sqr + g_stir*g_stir +num_steps = num_steps + 1 +used = send_data(id_stir, g_stir, Time) + +end subroutine stirring +!================================================================================================================================ +subroutine stirring_end + +if(.not.module_is_initialized) return + +if(amplitude == 0.0) return ! stirring does nothing unless amplitude is non-zero + +g_stir_sqr = g_stir_sqr/num_steps +used = send_data(id_g_stir_sqr, g_stir_sqr) + +call write_data('RESTART/stirring.res.nc', 'stir_real', real(s_stir), spectral_domain) +call write_data('RESTART/stirring.res.nc', 'stir_imag', aimag(s_stir), spectral_domain) +call random_seed(get=seed) +call write_data('RESTART/stirring.res.nc', 'ran_nmbr_seed', seed, no_domain=.true.) + +deallocate(wave_mask, s_stir, localize, g_stir_sqr) +module_is_initialized = .false. + +end subroutine stirring_end +!================================================================================================================================ + +end module stirring_mod diff --git a/src/atmos_spectral_shallow/atmosphere.F90 b/src/atmos_spectral_shallow/atmosphere.F90 new file mode 100644 index 000000000..7336f9b72 --- /dev/null +++ b/src/atmos_spectral_shallow/atmosphere.F90 @@ -0,0 +1,281 @@ +Module atmosphere_mod + +!----------------------------------------------------------------------- +! GNU General Public License +! +! This program is free software; you can redistribute it and/or modify it and +! are expected to follow the terms of the GNU General Public License +! as published by the Free Software Foundation; either version 2 of +! the License, or (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, but WITHOUT +! ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +! or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +! License for more details. +! +! For the full text of the GNU General Public License, +! write to: Free Software Foundation, Inc., +! 675 Mass Ave, Cambridge, MA 02139, USA. +! or see: http://www.gnu.org/licenses/gpl.html +!----------------------------------------------------------------------- + +!========================================================================= + +use fms_mod, only: open_namelist_file, & + open_restart_file, & + file_exist, & + check_nml_error, & + error_mesg, & + FATAL, WARNING, & + write_version_number, & + mpp_pe, & + mpp_root_pe, & + close_file, & + stdlog + +use mpp_mod, only: mpp_max + +use constants_mod, only: radius_earth => radius, & + omega_earth => omega + +use transforms_mod, only : get_deg_lon, & + get_deg_lat, & + get_grid_boundaries, & + get_grid_domain, & + get_spec_domain, & + area_weighted_global_mean, & + atmosphere_domain + +use time_manager_mod, only : time_type, & + set_time, & + get_time, & + interval_alarm, & + operator(+), & + operator(<), & + operator(==) + +use shallow_dynamics_mod, only : shallow_dynamics_init, & + shallow_dynamics, & + shallow_dynamics_end, & + dynamics_type + +use shallow_physics_mod, only : shallow_physics_init, & + shallow_physics, & + shallow_physics_end, & + phys_type + +use shallow_diagnostics_mod, only : shallow_diagnostics_init, & + shallow_diagnostics + +use stirring_mod, only: stirring_init + +!======================================================================== +implicit none +private +!======================================================================== + +! version information +!======================================================================== +character(len=128) :: version = '$Id: atmosphere.F90,v 14.0 2007/03/15 22:13:18 fms Exp $' +character(len=128) :: tagname = '$Name: siena_201207 $' +!======================================================================== + +public :: atmosphere_init, & + atmosphere, & + atmosphere_end, & + atmosphere_domain + +!======================================================================== + +integer, parameter :: num_time_levels = 2 + +integer :: unit, seconds, days +integer :: pe, npes +integer :: previous, current, future +logical :: root + +integer :: dt_integer +real :: dt_real +type(time_type) :: dt_time_type, Time_init, Time_step + +real :: delta_t ! = 2*dt_real for leapfrog step + +integer, dimension(2) :: axes + +type(phys_type), save :: Phys +type(dynamics_type), save :: Dyn + +integer :: is, ie, js, je, ms, me, ns, ne +integer :: num_lon, num_lat +integer, dimension(4) :: axis_id ! axes identifiers + +logical :: module_is_initialized =.false. + + +integer :: print_interval +! namelist +!======================================================================== +namelist /atmosphere_nml/ print_interval +!======================================================================== + +contains +!======================================================================= + +subroutine atmosphere_init(Time_init_in, Time, Time_step_in) + +type (time_type), intent(in) :: Time_init_in, Time, Time_step_in + +integer :: i, j, n, nn, ierr, io, unit, id_lon, id_lat, id_lonb, id_latb +integer :: nlon, nlat + +pe = mpp_pe() +root = (pe == mpp_root_pe()) + +Time_step = Time_step_in +call get_time(Time_step, seconds, days) +dt_integer = 86400*days + seconds +dt_real = float(dt_integer) +dt_time_type = Time_step +Time_init = Time_init_in + +! read the namelist + +if (file_exist('input.nml')) then + unit = open_namelist_file () + ierr=1 + do while (ierr /= 0) + read (unit, nml=atmosphere_nml, iostat=io, end=10) + ierr = check_nml_error (io, 'atmosphere_nml') + enddo + 10 call close_file (unit) +endif +call write_version_number(version, tagname) +if (root) write (stdlog(), nml=atmosphere_nml) + +call shallow_dynamics_init (Dyn, Time, Time_init, dt_real) + +call get_grid_domain(is,ie,js,je) +call get_spec_domain(ms,me,ns,ne) + +num_lon = Dyn%num_lon +num_lat = Dyn%num_lat + +nlon = ie+1-is ! size of grid on each processor +nlat = je+1-js + +call shallow_physics_init(Phys) +call shallow_diagnostics_init(Time, num_lon, num_lat, id_lon, id_lat, id_lonb, id_latb) +call stirring_init(dt_real, Time, id_lon, id_lat, id_lonb, id_latb) + +if(Time == Time_init) then + previous = 1 + current = 1 +else + previous = 1 + current = 2 +endif + +module_is_initialized = .true. + +return +end subroutine atmosphere_init + +!===================================================================== + +subroutine atmosphere(Time) + +type (time_type), intent(in) :: Time +integer :: day, second, dt + + +if(.not.module_is_initialized) then + call error_mesg('atmosphere', & + 'atmosphere_init has not been called', FATAL) +end if + +call get_time(Time_step, second, day) +dt = second + 86400*day + +Dyn%Tend%u = 0.0 +Dyn%Tend%v = 0.0 +Dyn%Tend%h = 0.0 +if(Dyn%grid_tracer) Dyn%Tend%tr = 0.0 +if(Dyn%spec_tracer) Dyn%Tend%trs = 0.0 + +if(Time == Time_init) then + delta_t = dt_real + future = 2 +else + delta_t = 2.0*dt_real + future = previous +endif + +call shallow_physics(Time, & + Dyn%Tend%u, Dyn%Tend%v, Dyn%Tend%h, & + Dyn%Grid%u, Dyn%Grid%v, Dyn%Grid%h, & + delta_t, previous, current, & + Phys) + +call shallow_dynamics(Time, Time_init, & + Dyn, previous, current, future, delta_t) + +previous = current +current = future + +call shallow_diagnostics (Time+Time_step, Dyn%Grid, Phys, current) + +call get_time(Time+Time_step, second, day) +if(mod(second+86400*day, print_interval) < dt) & + call global_diag(second, day, current) + +return +end subroutine atmosphere + +!======================================================================================= + +subroutine global_diag(second, day, current) + +integer, intent(in) :: second, day, current + +real :: enstrophy, div_squared, max_Froude +real, dimension(size(Dyn%Grid%u,1), size(Dyn%Grid%u,2)) :: speed + +enstrophy = & +area_weighted_global_mean(Dyn%grid%vor(:,:,current)*Dyn%grid%vor(:,:,current)) + +div_squared = & +area_weighted_global_mean(Dyn%grid%div(:,:,current)*Dyn%grid%div(:,:,current)) + +speed = Dyn%Grid%u(:,:,current)*Dyn%Grid%u(:,:,current) +& + Dyn%Grid%v(:,:,current)*Dyn%Grid%v(:,:,current) +max_Froude = maxval(speed/Dyn%Grid%h(:,:,current)) +call mpp_max(max_Froude) + +if(root) then + write(*,1000) day, second, enstrophy, div_squared, max_Froude +end if +1000 format(1x, 'day =',i6,2x,'second =', i6, & + 2x,'enstrophy = ',e13.6,3x,'div_squared = ',e13.6, 3x, & + 'max_Froude = ', e10.3) + +return +end subroutine global_diag + +!=============================================================================== +subroutine atmosphere_end + +if(.not.module_is_initialized) then + call error_mesg('atmosphere_end', & + 'atmosphere_init has not been called.', FATAL) +end if + +call shallow_physics_end (Phys) +call shallow_dynamics_end (Dyn, previous, current) + +module_is_initialized = .false. + +return +end subroutine atmosphere_end + +!======================================================================================= +end module atmosphere_mod diff --git a/src/atmos_spectral_shallow/atmosphere.html b/src/atmos_spectral_shallow/atmosphere.html new file mode 100644 index 000000000..13e269faf --- /dev/null +++ b/src/atmos_spectral_shallow/atmosphere.html @@ -0,0 +1,164 @@ + +module atmosphere_mod + + +
+ +

module atmosphere_mod

+ + +
+     Contact: Isaac Held
+     Reviewers: Peter Phillipps
+
+
+ + +
+

OVERVIEW

+ +
+   A spectral transform model for the shallow water equations on the sphere
+
+
+ + +
+

DESCRIPTION

+ +
+   Integrates the shallow water equations for hydrostatic flow in a thin layer
+   of homogeneous fluid on the sphere, using the spectral transform technique.
+   Also allows for the inclusion of a passive tracer advected by the 
+   spectral advection algorithm as the vorticity, and a gridpoint tracer 
+   advected with a finite volume algorithm on the transform grid.  
+   The default experiment is forced by a "monsoonal" mass source, starting
+   from a state of rest.
+   
+   For a full description of the model and algorithms used, see shallow.ps
+
+   The interfaces in this module are the generic intefaces required by the
+   main program that can be used to drive various idealized atmospheric
+   models within FMS. Model resolution and related parameters are set in
+   namelists within the modules shallow_xxx.
+
+ + + +
+

OTHER MODULES USED

+ +
+     fms_mod
+     constants_mod
+     transforms_mod
+     time_manager_mod
+     diag_manager_mod
+     shallow_dynamics_mod
+     shallow_physics_mod
+     shallow_diagnostics_mod
+
+
+ + +
+

PUBLIC INTERFACE

+ +
+  use atmosphere_mod [,only: atmosphere_init,       
+                             atmosphere,
+			     atmosphere_end]
+
+
+ + +
+

PUBLIC DATA

+ +
+  There are no public data types
+
+
+ + +
+

PUBLIC ROUTINES

+ +
+subroutine atmosphere_init. Initializes the model.
+subroutine atmosphere.      Integrates forward one time step
+subroutine atmosphere_end.  Terminates model, cleaning up memory and finalizing diagnostics.
+
+
+
+ subroutine atmosphere_init(Time_init, Time, Time_step)
+
+   input:
+
+   type(time_type) :: Time_init -- Initial model time
+
+   type(time_type) :: Time      -- Model time
+
+   type(time_type) :: Time_step -- Time step
+
+   When Time=Time_init, the first time step is a forward
+   step rather than leap frog because a cold start is assumed.
+
+   The FMS main program that runs the solo atmospheric models
+   obtains Time_init from the diag_table and Time from its namelist.
+       
+
+
+ + + subroutine atmosphere(Time) + + input: + + type(time_type) :: Time -- Model time + + Integrates forward one time step + + +
+ + subroutine atmosphere_end + + No calling arguments. + + Terminates model, cleaning up memory and finalizing diagnostics + +
+
+ + +

NAMELIST

+ +
+&atmosphere_nml
+
+   print_interval, integer : time interval in seconds 
+   between prints of global mean energy and enstrophy to standard output
+
+
+ + +
+

ERROR MESSAGES

+ +
+  Fatal error message if subroutine atmosphere or atmosphere_end
+  is called prior to atmosphere_init.
+
+
+ +
+ + diff --git a/src/atmos_spectral_shallow/shallow.pdf b/src/atmos_spectral_shallow/shallow.pdf new file mode 100644 index 000000000..3ae5dbd1a Binary files /dev/null and b/src/atmos_spectral_shallow/shallow.pdf differ diff --git a/src/atmos_spectral_shallow/shallow_diagnostics.F90 b/src/atmos_spectral_shallow/shallow_diagnostics.F90 new file mode 100644 index 000000000..3ea5dc0a8 --- /dev/null +++ b/src/atmos_spectral_shallow/shallow_diagnostics.F90 @@ -0,0 +1,259 @@ + +module shallow_diagnostics_mod + +!----------------------------------------------------------------------- +! GNU General Public License +! +! This program is free software; you can redistribute it and/or modify it and +! are expected to follow the terms of the GNU General Public License +! as published by the Free Software Foundation; either version 2 of +! the License, or (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, but WITHOUT +! ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +! or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +! License for more details. +! +! For the full text of the GNU General Public License, +! write to: Free Software Foundation, Inc., +! 675 Mass Ave, Cambridge, MA 02139, USA. +! or see: http://www.gnu.org/licenses/gpl.html +!----------------------------------------------------------------------- + +use fms_mod, only: write_version_number + +use transforms_mod, only: get_grid_boundaries, & + get_deg_lon, & + get_deg_lat, & + get_grid_domain, & + get_spec_domain, & + grid_domain, & + area_weighted_global_mean + +use diag_manager_mod, only: diag_axis_init, & + register_diag_field, & + register_static_field, & + send_data + +use time_manager_mod, only: time_type, & + get_time + + +use shallow_physics_mod, only: phys_type +use shallow_dynamics_mod, only: grid_type + + +implicit none +private + +public :: shallow_diagnostics_init, & + shallow_diagnostics + +character(len=84), parameter :: version = '$Id: shallow_diagnostics.F90,v 10.0 2003/10/24 22:01:02 fms Exp $' +character(len=84), parameter :: tagname = '$Name: siena_201207 $' +character(len=8) :: axiset = 'shallow' +character(len=84) :: mod_name = 'shallow_diagnostics' + +logical :: module_is_initialized = .false. + +integer :: id_vor, id_stream, id_pv, id_u, id_v, id_div, id_h, id_trs, id_tr, id_d_geopot, id_u_sqd, id_v_sqd, id_h_sqd, id_u_sqd_mean, id_v_sqd_mean, id_h_sqd_mean, id_ekin, id_ekin_density, id_eq_geopot, id_e_kin_real_units, id_e_pot_real_units, id_e_tot_real_units, id_u_rms, id_vcomp_vor, id_ucomp_vcomp + +integer :: is, ie, js, je + +contains + +!----------------------------------------------------------------------------------------------------------------- +subroutine shallow_diagnostics_init(Time, lon_max, lat_max, id_lon, id_lat, id_lonb, id_latb) + +type(time_type), intent(in) :: Time +integer, intent(in) :: lon_max, lat_max +integer, intent(out):: id_lon, id_lat, id_lonb, id_latb + +real, dimension(lon_max ) :: lon +real, dimension(lon_max+1) :: lonb +real, dimension(lat_max ) :: lat +real, dimension(lat_max+1) :: latb + +integer, dimension(2) :: axis_2d + +integer :: log_unit +integer :: namelist_unit, ierr, io +real :: rad_to_deg +logical :: used + +call write_version_number(version, tagname) + +call get_grid_domain(is, ie, js, je) + +rad_to_deg = 45./atan(1.) +call get_grid_boundaries(lonb,latb,global=.true.) +call get_deg_lon(lon) +call get_deg_lat(lat) + +id_lonb=diag_axis_init('lonb', rad_to_deg*lonb, 'degrees_E', 'x', 'longitude edges', set_name=axiset, Domain2=grid_domain) +id_latb=diag_axis_init('latb', rad_to_deg*latb, 'degrees_N', 'y', 'latitude edges', set_name=axiset, Domain2=grid_domain) +id_lon =diag_axis_init('lon', lon, 'degrees_E', 'x', 'longitude', set_name=axiset, Domain2=grid_domain, edges=id_lonb) +id_lat =diag_axis_init('lat', lat, 'degrees_N', 'y', 'latitude', set_name=axiset, Domain2=grid_domain, edges=id_latb) + +axis_2d(1) = id_lon +axis_2d(2) = id_lat + +id_u = register_diag_field(mod_name, 'ucomp' , axis_2d, Time, 'u_wind' , 'm/s' ) +id_v = register_diag_field(mod_name, 'vcomp' , axis_2d, Time, 'v_wind' , 'm/s' ) +id_vor = register_diag_field(mod_name, 'vor' , axis_2d, Time, 'relative vorticity' , '1/s' ) +id_div = register_diag_field(mod_name, 'div' , axis_2d, Time, 'divergence' , '1/s' ) +id_h = register_diag_field(mod_name, 'h' , axis_2d, Time, 'geopotential' , 'm2/s2' ) +id_pv = register_diag_field(mod_name, 'pv_corrected' , axis_2d, Time, 'potential vorticity' , 's/m2' ) +id_stream = register_diag_field(mod_name, 'stream', axis_2d, Time, 'streamfunction' , 'm^2/s' ) +id_trs = register_diag_field(mod_name, 'trs' , axis_2d, Time, 'spectral tracer' , 'none' ) +id_tr = register_diag_field(mod_name, 'tr' , axis_2d, Time, 'grid tracer' , 'none' ) +id_d_geopot = register_diag_field(mod_name, 'deep_geopot', axis_2d, Time, 'deep_geopot' , 'm2/s2') + +id_u_sqd = register_diag_field(mod_name, 'ucomp_sqd' , axis_2d, Time, 'u_wind_sqd' , 'm^2/s^2' ) +id_v_sqd = register_diag_field(mod_name, 'vcomp_sqd' , axis_2d, Time, 'v_wind_sqd' , 'm^2/s^2' ) +id_h_sqd = register_diag_field(mod_name, 'h_sqd' , axis_2d, Time, 'geopotential_sqd' , 'm4/s4' ) + +id_u_sqd_mean = register_diag_field(mod_name, 'ucomp_sqd_mean' , Time, 'u_wind_sqd_mean' , 'm^2/s^2' ) +id_v_sqd_mean = register_diag_field(mod_name, 'vcomp_sqd_mean' , Time, 'v_wind_sqd_mean' , 'm^2/s^2' ) +id_h_sqd_mean = register_diag_field(mod_name, 'h_sqd_mean' , Time, 'geopotential_sqd_mean' , 'm4/s4' ) + +id_ekin = register_diag_field(mod_name, 'e_kin' , Time, 'kinetic_energy' , 'm^2/s^2' ) +id_ekin_density = register_diag_field(mod_name, 'e_kin_density' , Time, 'kinetic_energy_density' , 'm^3/s^2' ) + +id_eq_geopot = register_diag_field(mod_name, 'eq_geopot' , Time, 'equilibrium_geopotential' , 'm^2/s^2' ) +id_e_kin_real_units = register_diag_field(mod_name, 'e_kin_real_units' , Time, 'e_kin_real_units' , 'J/kg' ) +id_e_pot_real_units = register_diag_field(mod_name, 'e_pot_real_units' , Time, 'e_pot_real_units' , 'J/kg' ) +id_e_tot_real_units = register_diag_field(mod_name, 'e_tot_real_units' , Time, 'e_tot_real_units' , 'J/kg' ) + +id_u_rms = register_diag_field(mod_name, 'u_rms' , Time, 'r_rms' , 'm/s' ) + +id_vcomp_vor = register_diag_field(mod_name, 'vcomp_vor' , axis_2d, Time, 'vcomp * relative vorticity' , 'm/s^2' ) + +id_ucomp_vcomp = register_diag_field(mod_name, 'ucomp_vcomp' , axis_2d, Time, 'ucomp * vcomp' , 'm^2/s^2' ) + +module_is_initialized = .true. + +return +end subroutine shallow_diagnostics_init + +!-------------------------------------------------------------------------------------------- + +subroutine shallow_diagnostics(Time, Grid, Phys, time_index) + +type(time_type), intent(in) :: Time +type(phys_type), intent(in) :: Phys +type(grid_type), intent(in) :: Grid +integer, intent(in) :: time_index + +real :: e_kin_real_units, e_pot_real_units, e_tot_real_units, eq_geopot + +logical :: used + +if(id_u > 0) used = send_data(id_u , Grid%u (:,:, time_index) , time) +if(id_v > 0) used = send_data(id_v , Grid%v (:,:, time_index) , time) +if(id_vor > 0) used = send_data(id_vor , Grid%vor (:,:, time_index) , time) +if(id_div > 0) used = send_data(id_div , Grid%div (:,:, time_index) , time) +if(id_h > 0) used = send_data(id_h , Grid%h (:,:, time_index) , time) +if(id_pv > 0) used = send_data(id_pv , Grid%pv (:,:) , time) +if(id_stream > 0) used = send_data(id_stream , Grid%stream (:,:) , time) +if(id_tr > 0) used = send_data(id_tr , Grid%tr (:,:, time_index) , time) +if(id_trs > 0) used = send_data(id_trs , Grid%trs (:,:, time_index) , time) +if(id_d_geopot > 0) used = send_data(id_d_geopot, Grid%deep_geopot (:,:) , time) + +if (id_u_sqd > 0) then + used = send_data(id_u_sqd , Grid%u (:,:, time_index)**2 , time) +endif + +if (id_v_sqd > 0) then + used = send_data(id_v_sqd , Grid%v (:,:, time_index)**2 , time) +endif + +if (id_h_sqd > 0) then + used = send_data(id_h_sqd , Grid%h (:,:, time_index)**2 , time) +endif + +if (id_u_sqd_mean > 0) then + used = send_data(id_u_sqd_mean , area_weighted_global_mean(Grid%u (:,:, time_index)**2) , time) +endif + +if (id_v_sqd_mean > 0) then + used = send_data(id_v_sqd_mean , area_weighted_global_mean(Grid%v (:,:, time_index)**2) , time) +endif + +if (id_h_sqd_mean > 0) then + used = send_data(id_h_sqd_mean , area_weighted_global_mean(Grid%h (:,:, time_index)**2) , time) +endif + +if (id_ekin > 0) then + used = send_data(id_ekin , 0.5*(area_weighted_global_mean(Grid%u (:,:, time_index)**2) + area_weighted_global_mean(Grid%v (:,:, time_index)**2)) , time) +endif + +if (id_ekin_density > 0) then + used = send_data(id_ekin_density , 0.5*(area_weighted_global_mean(Grid%h (:,:, time_index)*(Grid%u (:,:, time_index)**2)) + area_weighted_global_mean(Grid%h (:,:, time_index)*(Grid%v (:,:, time_index)**2))) , time) +endif + +eq_geopot = 0. +e_kin_real_units = 0. +e_pot_real_units = 0. + +if (id_eq_geopot > 0) then + + eq_geopot = area_weighted_global_mean(Grid%h (:,:, time_index)) + used = send_data(id_eq_geopot , eq_geopot, time) + +endif + + +if (id_e_kin_real_units > 0) then + + if (eq_geopot == 0.) eq_geopot = area_weighted_global_mean(Grid%h (:,:, time_index)) + + e_kin_real_units = 0.5*(area_weighted_global_mean(Grid%h (:,:, time_index)*(Grid%u (:,:, time_index)**2)) + area_weighted_global_mean(Grid%h (:,:, time_index)*(Grid%v (:,:, time_index)**2))) / eq_geopot + + used = send_data(id_e_kin_real_units , e_kin_real_units, time) + +endif + +if (id_e_pot_real_units > 0) then + + if (eq_geopot == 0.) eq_geopot = area_weighted_global_mean(Grid%h (:,:, time_index)) + + e_pot_real_units = 0.5*(area_weighted_global_mean(Grid%h (:,:, time_index)**2.)) / eq_geopot + + used = send_data(id_e_pot_real_units , e_pot_real_units, time) + +endif + +if (id_e_tot_real_units > 0) then + + if (eq_geopot == 0.) eq_geopot = area_weighted_global_mean(Grid%h (:,:, time_index)) + + if (e_kin_real_units == 0.) then + e_kin_real_units = 0.5*(area_weighted_global_mean(Grid%h (:,:, time_index)*(Grid%u (:,:, time_index)**2)) + area_weighted_global_mean(Grid%h (:,:, time_index)*(Grid%v (:,:, time_index)**2))) / eq_geopot + endif + + if (e_pot_real_units == 0.) then + e_pot_real_units = 0.5*(area_weighted_global_mean(Grid%h (:,:, time_index)**2.)) / eq_geopot + endif + + e_tot_real_units = e_kin_real_units + e_pot_real_units + + used = send_data(id_e_tot_real_units , e_tot_real_units, time) + +endif + +if (id_u_rms > 0) then + + used = send_data(id_u_rms , (area_weighted_global_mean(Grid%u (:,:, time_index)**2) + area_weighted_global_mean(Grid%v (:,:, time_index)**2))**0.5, time) + +endif + + +if(id_vcomp_vor > 0) used = send_data(id_vcomp_vor , Grid%v (:,:, time_index) * Grid%vor (:,:, time_index) , time) +if(id_ucomp_vcomp > 0) used = send_data(id_ucomp_vcomp , Grid%u (:,:, time_index) * Grid%v (:,:, time_index) , time) + +return +end subroutine shallow_diagnostics +!-------------------------------------------------------------------------------------------- + +end module shallow_diagnostics_mod diff --git a/src/atmos_spectral_shallow/shallow_diagnostics.html b/src/atmos_spectral_shallow/shallow_diagnostics.html new file mode 100644 index 000000000..65db99f52 --- /dev/null +++ b/src/atmos_spectral_shallow/shallow_diagnostics.html @@ -0,0 +1,150 @@ + +module shallow_diagnostics_mod + + +
+ + +

module shallow_diagnostics_mod

+ + +
+     Contact: Isaac Held
+     Reviewers: Peter Phillipps
+
+
+
+ + +
+

OVERVIEW

+ +
+
+   The diagnostics module for the model that solves the shallow water
+   equation on the sphere  
+   
+
+
+ + +
+

DESCRIPTION

+ +
+
+   Using the diagnostics manager, creates output files for the shallow model.
+   Variables currently available for output are
+       zonal wind 
+       meridional wind 
+       relative vorticity
+       absolute vorticity
+       streamfunction
+       spectral tracer in grid domain
+       grid tracer
+       
+   Whether or not these fields are actually output, the location of the 
+   output, the frequency of output, whether or not the output is averaged
+   in time or an instantaneous snapshot, is controlled by a 
+   diag_table file utilized by the diagnostics manager module
+       
+   One can add other diagnostics by following the (somewhat convoluted)
+       pattern within the program
+
+
+
+
+ + +
+

OTHER MODULES USED

+ +
+
+     diag_manaager_mod
+     transforms_mod
+     time_manager_mod
+     shallow_dynamics_mod
+     shallow_physics_mod
+
+
+
+ + +
+

PUBLIC INTERFACE

+ +
+
+  use shallow_diagnostics_mod [,only: shallow_diagnostics_init,       
+                                         shallow_diagnostics]
+                                
+
+
+ + + +
+

PUBLIC ROUTINES

+ +
+
+subroutine  shallow_diagnostics_init
+subroutine  shallow_diagnostics
+
+
+
+
+ subroutine shallow_diagnostics_init(Time, num_lon, num_lat)
+ 
+   type(time_type)    , intent(in)     :: Time
+         current time 
+   integer, intent(in) :: num_lon, num_lat
+      num_lon = number of longitudes in global domain
+      num_lat = number of latitudes in global domain
+         
+
+   Initializes module
+
+
+
+
+ + + + subroutine shallow_diagnostics (Time, Grid, Phys, time_index) + + type(time_type), intent(in) :: Time + type(phys_type), intent(in) :: Phys + type(grid_type), intent(in) :: Grid + integer, intent(in) :: time_index + + phys_type is defined in shallow_physics_mod; ; + + grid_type is defined in shallow_dynamics_mod: + Grid contains all of the fields to be output + + many of the grid fields in grid_type are dimensioned (lon, lat, time_index) + where time_index = 1 or 2 -- the two time levels needed to update the + state of the model using a leapfrog step are toggled between (:,:,1) + and (:,:,2). The input time_index (which must equal either 1 or 2) + determines which of these two fields is output) + + (this is confusing -- the calling program needs to know what has + been placed in which slot -- it would be better to store this + information within the data type) + + + + +
+ + + diff --git a/src/atmos_spectral_shallow/shallow_dynamics.F90 b/src/atmos_spectral_shallow/shallow_dynamics.F90 new file mode 100644 index 000000000..14b7e89db --- /dev/null +++ b/src/atmos_spectral_shallow/shallow_dynamics.F90 @@ -0,0 +1,730 @@ +module shallow_dynamics_mod + +!----------------------------------------------------------------------- +! GNU General Public License +! +! This program is free software; you can redistribute it and/or modify it and +! are expected to follow the terms of the GNU General Public License +! as published by the Free Software Foundation; either version 2 of +! the License, or (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, but WITHOUT +! ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +! or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +! License for more details. +! +! For the full text of the GNU General Public License, +! write to: Free Software Foundation, Inc., +! 675 Mass Ave, Cambridge, MA 02139, USA. +! or see: http://www.gnu.org/licenses/gpl.html +!----------------------------------------------------------------------- + +use fms_mod, only: open_namelist_file, & + open_restart_file, & + file_exist, & + check_nml_error, & + error_mesg, & + FATAL, & + write_version_number, & + mpp_pe, & + mpp_root_pe, & + read_data, & + write_data, & + set_domain, & + close_file, & + stdlog + +use time_manager_mod, only : time_type, & + get_time, & + operator(==), & + operator(-) + +use constants_mod, only : radius, omega, DEG_TO_RAD + +use transforms_mod, only: transforms_init, transforms_end, & + get_grid_boundaries, horizontal_advection, & + trans_spherical_to_grid, trans_grid_to_spherical, & + compute_laplacian, get_eigen_laplacian, & + get_sin_lat, get_cos_lat, & + get_deg_lon, get_deg_lat, & + get_grid_domain, get_spec_domain, & + spectral_domain, grid_domain, & + vor_div_from_uv_grid, uv_grid_from_vor_div, & + area_weighted_global_mean + +use spectral_damping_mod, only: spectral_damping_init, compute_spectral_damping + +use leapfrog_mod, only: leapfrog + +use fv_advection_mod, only : fv_advection_init, a_grid_horiz_advection + +use stirring_mod, only : stirring, stirring_end + +use interpolator_mod, only: interpolate_type,interpolator_init,CONSTANT,interpolator + +!====================================================================================== +implicit none +private +!====================================================================================== + +public :: shallow_dynamics_init, shallow_dynamics, shallow_dynamics_end, & + dynamics_type, grid_type, spectral_type, tendency_type + + +! version information +!=================================================================== +character(len=128) :: version = '$Id: shallow_dynamics.F90,v 10.0 2003/10/24 22:01:02 fms Exp $' +character(len=128) :: tagname = '$Name: siena_201207 $' +!=================================================================== + +type grid_type + real, pointer, dimension(:,:,:) :: u=>NULL(), v=>NULL(), vor=>NULL(), div=>NULL(), h=>NULL(), trs=>NULL(), tr=>NULL() + real, pointer, dimension(:,:) :: stream=>NULL(), pv=>NULL(), deep_geopot=>NULL() +end type +type spectral_type + complex, pointer, dimension(:,:,:) :: vor=>NULL(), div=>NULL(), h=>NULL(), trs=>NULL() +end type +type tendency_type + real, pointer, dimension(:,:) :: u=>NULL(), v=>NULL(), h=>NULL(), trs=>NULL(), tr=>NULL() +end type +type dynamics_type + type(grid_type) :: grid + type(spectral_type) :: spec + type(tendency_type) :: tend + integer :: num_lon, num_lat + logical :: grid_tracer, spec_tracer +end type + + + +integer, parameter :: num_time_levels = 2 + +integer :: is, ie, js, je, ms, me, ns, ne + +logical :: module_is_initialized = .false. + +real, allocatable, dimension(:) :: sin_lat, cos_lat, rad_lat, deg_lat, deg_lon, & + coriolis + +real, allocatable, dimension(:,:) :: eigen + +integer :: pe, npes + + +! namelist parameters with default values + +integer :: num_lon = 256 +integer :: num_lat = 128 +integer :: num_fourier = 85 +integer :: num_spherical = 86 +integer :: fourier_inc = 1 +integer :: cutoff_wn = 30 +! (these define a standard T85 model) + +logical :: check_fourier_imag = .false. +logical :: south_to_north = .true. +logical :: triang_trunc = .true. + +real :: robert_coeff = 0.04 +real :: robert_coeff_tracer = 0.04 +real :: longitude_origin = 0.0 +real :: raw_filter_coeff = 1.0 + +character(len=64) :: damping_option = 'resolution_dependent' +integer :: damping_order = 4 +real :: damping_coeff = 1.e-04 +real :: h_0 = 3.e04 + +real :: u_deep_mag = 0. +real :: n_merid_deep_flow = 3. +real :: u_upper_mag_init = 0. + +logical :: spec_tracer = .true. +logical :: grid_tracer = .true. + +!Options for injecting an initial vortex pair +real :: lon_centre_init_cyc = 0. +real :: lat_centre_init_cyc = 60. +real :: lon_centre_init_acyc = 180. +real :: lat_centre_init_acyc = 60. +real :: init_vortex_radius_deg = 5. +real :: init_vortex_vor_f = 0.5 +real :: init_vortex_h_h_0 = 0.1 +logical :: add_initial_vortex_pair = .false. +logical :: add_initial_vortex_as_height = .true. + +logical :: initial_condition_from_input_file=.false. +character(len=64) :: init_cond_file = 'init_cond_h_vor_div' +character(len=64) :: input_file_div_name = 'div' +character(len=64) :: input_file_height_name = 'height' +character(len=64) :: input_file_vor_name = 'vor' + +real, dimension(2) :: valid_range_v = (/-1.e3,1.e3/) + +type(interpolate_type),save :: init_cond_interp + +namelist /shallow_dynamics_nml/ check_fourier_imag, & + south_to_north, triang_trunc, & + num_lon, num_lat, num_fourier, & + num_spherical, fourier_inc, & + longitude_origin, damping_option, & + damping_order, damping_coeff, & + robert_coeff, robert_coeff_tracer, & + h_0, spec_tracer, grid_tracer, & + valid_range_v, cutoff_wn, & + raw_filter_coeff, & + u_deep_mag, n_merid_deep_flow, & + u_upper_mag_init, & + lon_centre_init_cyc, & + lat_centre_init_cyc, & + lon_centre_init_acyc, & + lat_centre_init_acyc, & + init_vortex_radius_deg, & + init_vortex_vor_f, & + init_vortex_h_h_0, & + add_initial_vortex_pair, & + add_initial_vortex_as_height, & + initial_condition_from_input_file, & + init_cond_file, & + input_file_div_name, & + input_file_height_name, & + input_file_vor_name + + +contains + +!======================================================================================= + +subroutine shallow_dynamics_init (Dyn, Time, Time_init, dt_real) + +type(dynamics_type), intent(inout) :: Dyn +type(time_type) , intent(in) :: Time, Time_init +real , intent(in) :: dt_real + +integer :: i, j + +real, allocatable, dimension(:) :: glon_bnd, glat_bnd +real, allocatable, dimension(:,:) :: rad_lonb_2d, rad_latb_2d +real :: xx, yy, dd, deep_geopot_global_mean, radius_loc_cyc, radius_loc_acyc + +integer :: ierr, io, unit, id_lon, id_lat, id_lonb, id_latb +logical :: root + +! < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > + +call write_version_number (version, tagname) + +pe = mpp_pe() +root = (pe == mpp_root_pe()) + +if (file_exist('input.nml')) then + unit = open_namelist_file () + ierr=1 + do while (ierr /= 0) + read (unit, nml=shallow_dynamics_nml, iostat=io, end=10) + ierr = check_nml_error (io, 'shallow_dynamics_nml') + enddo + 10 call close_file (unit) +endif + +if (root) write (stdlog(), nml=shallow_dynamics_nml) + +call transforms_init(radius, num_lat, num_lon, num_fourier, fourier_inc, num_spherical, & + south_to_north=south_to_north, & + triang_trunc=triang_trunc, & + longitude_origin=longitude_origin ) + +call get_grid_domain(is,ie,js,je) +call get_spec_domain(ms,me,ns,ne) + +Dyn%num_lon = num_lon +Dyn%num_lat = num_lat +Dyn%spec_tracer = spec_tracer +Dyn%grid_tracer = grid_tracer + +allocate (sin_lat (js:je)) +allocate (cos_lat (js:je)) +allocate (deg_lat (js:je)) +allocate (deg_lon (is:ie)) +allocate (coriolis (js:je)) + +call get_deg_lon (deg_lon) +call get_deg_lat (deg_lat) +call get_sin_lat (sin_lat) +call get_cos_lat (cos_lat) + +allocate (glon_bnd (num_lon + 1)) +allocate (glat_bnd (num_lat + 1)) +call get_grid_boundaries (glon_bnd, glat_bnd, global=.true.) +allocate (rad_lonb_2d(is:ie+1, js:je+1)) +allocate (rad_latb_2d(is:ie+1, js:je+1)) + +do i = is,ie+1 + rad_lonb_2d(i,:) = glon_bnd(i) +enddo + +do j = js,je+1 + rad_latb_2d(:,j) = glat_bnd(j) +enddo + +coriolis = 2*omega*sin_lat + +call spectral_damping_init(damping_coeff, damping_order, damping_option, cutoff_wn, num_fourier, num_spherical, 1, 0., 0., 0.) + +allocate(eigen(ms:me,ns:ne)) +call get_eigen_laplacian(eigen) + +allocate (Dyn%spec%vor (ms:me, ns:ne, num_time_levels)) +allocate (Dyn%spec%div (ms:me, ns:ne, num_time_levels)) +allocate (Dyn%spec%h (ms:me, ns:ne, num_time_levels)) + +allocate (Dyn%grid%u (is:ie, js:je, num_time_levels)) +allocate (Dyn%grid%v (is:ie, js:je, num_time_levels)) +allocate (Dyn%grid%vor (is:ie, js:je, num_time_levels)) +allocate (Dyn%grid%div (is:ie, js:je, num_time_levels)) +allocate (Dyn%grid%h (is:ie, js:je, num_time_levels)) + +allocate (Dyn%tend%u (is:ie, js:je)) +allocate (Dyn%tend%v (is:ie, js:je)) +allocate (Dyn%tend%h (is:ie, js:je)) +allocate (Dyn%grid%stream (is:ie, js:je)) +allocate (Dyn%grid%pv (is:ie, js:je)) +allocate (Dyn%grid%deep_geopot(is:ie, js:je)) + + +call fv_advection_init(num_lon, num_lat, glat_bnd, 360./float(fourier_inc)) +if(Dyn%grid_tracer) then + allocate(Dyn%Grid%tr (is:ie, js:je, num_time_levels)) + allocate(Dyn%Tend%tr (is:ie, js:je)) +endif + +if(Dyn%spec_tracer) then + allocate(Dyn%Grid%trs (is:ie, js:je, num_time_levels)) + allocate(Dyn%Tend%trs (is:ie, js:je)) + allocate(Dyn%Spec%trs (ms:me, ns:ne, num_time_levels)) +endif + +if( initial_condition_from_input_file ) then + call interpolator_init( init_cond_interp, trim(init_cond_file)//'.nc', rad_lonb_2d, rad_latb_2d, data_out_of_bounds=(/CONSTANT/) ) +endif + + +do i = is, ie + Dyn%grid%deep_geopot(i, js:je) = -2.*omega * u_deep_mag * radius * (1./(1.-n_merid_deep_flow**2.))*(-cos(n_merid_deep_flow*DEG_TO_RAD*deg_lat(js:je))*cos(DEG_TO_RAD*deg_lat(js:je)) - n_merid_deep_flow * (sin(n_merid_deep_flow*DEG_TO_RAD*deg_lat(js:je))*sin(DEG_TO_RAD*deg_lat(js:je))-sin(n_merid_deep_flow*(2.*atan(1.))))) +enddo + +deep_geopot_global_mean = area_weighted_global_mean(Dyn%grid%deep_geopot(:,:)) +Dyn%grid%deep_geopot(:,:) = Dyn%grid%deep_geopot(:,:)-deep_geopot_global_mean + +if(Time == Time_init) then + + if (initial_condition_from_input_file) then + + call interpolator( init_cond_interp, Dyn%Grid%div(:,:,1), input_file_div_name ) + call interpolator( init_cond_interp, Dyn%Grid%h(:,:,1), input_file_height_name ) + call interpolator( init_cond_interp, Dyn%Grid%vor(:,:,1), input_file_vor_name ) + + Dyn%Grid%h(:,:,1) = Dyn%Grid%h(:,:,1)+h_0 !want to make sure that we keep h_0 consistent, so make sure mean of h input is zero, and add h_0 on afterwards... + + else + Dyn%Grid%div(:,:,1) = 0.0 + Dyn%Grid%h (:,:,1) = h_0 - Dyn%grid%deep_geopot(:,:) + + do i = is, ie + Dyn%Grid%vor(i,js:je,1) = -((u_upper_mag_init * n_merid_deep_flow)/radius) * sin(DEG_to_RAD * deg_lat(js:je)) + + if (add_initial_vortex_pair) then + + do j=js, je + + radius_loc_cyc = ((min((deg_lon(i)-lon_centre_init_cyc)**2., (deg_lon(i)-lon_centre_init_cyc-360.)**2.)+(deg_lat(j)-lat_centre_init_cyc)**2.)**0.5)/init_vortex_radius_deg + radius_loc_acyc = ((min((deg_lon(i)-lon_centre_init_acyc)**2., (deg_lon(i)-lon_centre_init_acyc-360.)**2.)+(deg_lat(j)-lat_centre_init_acyc)**2.)**0.5)/init_vortex_radius_deg + + + if(radius_loc_cyc.le.1.0 .and. radius_loc_acyc.le.1.0) then + call error_mesg('shallow_dynamics','Cannot initialise cyclone and anticyclone in same grid box ', FATAL) + endif + + if(add_initial_vortex_as_height) then + if (radius_loc_cyc.le.2.0) then + Dyn%Grid%h(i,j,1) = Dyn%Grid%h(i,j,1) + init_vortex_h_h_0 * -h_0 * exp(-radius_loc_cyc**2.) + elseif (radius_loc_acyc.le.2.0) then + Dyn%Grid%h(i,j,1) = Dyn%Grid%h(i,j,1) + init_vortex_h_h_0 * h_0 * exp(-radius_loc_acyc**2.) + endif + else + if (radius_loc_cyc.le.1.0) then + Dyn%Grid%vor(i,j,1) = init_vortex_vor_f * 2.*omega + elseif (radius_loc_acyc.le.1.0) then + Dyn%Grid%vor(i,j,1) = init_vortex_vor_f * -2.*omega + endif + + endif + + enddo + + + endif !add_initial_vortex_pair + enddo + + endif ! initial_condition_from_input_file + + call trans_grid_to_spherical(Dyn%Grid%vor(:,:,1), Dyn%Spec%vor(:,:,1)) + call trans_grid_to_spherical(Dyn%Grid%div(:,:,1), Dyn%Spec%div(:,:,1)) + call trans_grid_to_spherical(Dyn%Grid%h (:,:,1), Dyn%Spec%h (:,:,1)) + + call uv_grid_from_vor_div (Dyn%Spec%vor(:,:,1), Dyn%Spec%div(:,:,1), & + Dyn%Grid%u (:,:,1), Dyn%Grid%v (:,:,1)) + + if(Dyn%grid_tracer) then + Dyn%Grid%tr = 0.0 + do j = js, je + if(deg_lat(j) > 10.0 .and. deg_lat(j) < 20.0) Dyn%Grid%tr(:,j,1) = 1.0 + if(deg_lat(j) > 70.0 ) Dyn%Grid%tr(:,j,1) = -1.0 + end do + endif + + if(Dyn%spec_tracer) then + Dyn%Grid%trs = 0.0 + do j = js, je + if(deg_lat(j) > 10.0 .and. deg_lat(j) < 20.0) Dyn%Grid%trs(:,j,1) = 1.0 + if(deg_lat(j) > 70.0 ) Dyn%Grid%trs(:,j,1) = -1.0 + end do + call trans_grid_to_spherical(Dyn%Grid%trs(:,:,1), Dyn%Spec%trs(:,:,1)) + endif + +else + + call read_restart(Dyn) + +endif + +module_is_initialized = .true. + +return +end subroutine shallow_dynamics_init + +!======================================================================================== + +subroutine shallow_dynamics(Time, Time_init, Dyn, previous, current, future, delta_t) + +type(time_type) , intent(in) :: Time, Time_init +type(dynamics_type), intent(inout) :: Dyn +integer, intent(in ) :: previous, current, future +real, intent(in ) :: delta_t + +! < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > + +complex, dimension(ms:me, ns:ne) :: dt_vors, dt_divs, dt_hs, stream, bs, work +real, dimension(is:ie, js:je) :: vorg, bg, h_future, h_dt, dt_vorg +integer :: j + +! < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > < > + +if(.not.module_is_initialized) then + call error_mesg('shallow_dynamics','dynamics has not been initialized ', FATAL) +endif + + +do j = js,je + vorg(:,j) = Dyn%Grid%vor(:,j,current) + coriolis(j) +end do +Dyn%Tend%u = Dyn%Tend%u + vorg*Dyn%Grid%v(:,:,current) +Dyn%Tend%v = Dyn%Tend%v - vorg*Dyn%Grid%u(:,:,current) + +call vor_div_from_uv_grid (Dyn%Tend%u, Dyn%Tend%v, dt_vors, dt_divs) + +call horizontal_advection (Dyn%Spec%h(:,:,current), & + Dyn%Grid%u(:,:,current), Dyn%Grid%v(:,:,current), Dyn%Tend%h) + +Dyn%Tend%h = Dyn%Tend%h - Dyn%Grid%h(:,:,current)*Dyn%Grid%div(:,:,current) + +call trans_grid_to_spherical (Dyn%Tend%h, dt_hs) + +bg = (Dyn%Grid%h(:,:,current) + Dyn%grid%deep_geopot(:,:) + & + 0.5*(Dyn%Grid%u(:,:,current)**2 + Dyn%Grid%v(:,:,current)**2)) + +call trans_grid_to_spherical(bg, bs) +dt_divs = dt_divs - compute_laplacian(bs) + +call implicit_correction (dt_divs, dt_hs, Dyn%Spec%div, Dyn%Spec%h, & + delta_t, previous, current) + +call compute_spectral_damping(Dyn%Spec%vor(:,:,previous), dt_vors, delta_t) +call compute_spectral_damping(Dyn%Spec%div(:,:,previous), dt_divs, delta_t) +call compute_spectral_damping(Dyn%Spec%h (:,:,previous), dt_hs , delta_t) + +call stirring(Time, dt_vors) + +call leapfrog(Dyn%Spec%vor , dt_vors , previous, current, future, delta_t, robert_coeff, raw_filter_coeff) +call leapfrog(Dyn%Spec%div , dt_divs , previous, current, future, delta_t, robert_coeff, raw_filter_coeff) +call leapfrog(Dyn%Spec%h , dt_hs , previous, current, future, delta_t, robert_coeff, raw_filter_coeff) + +call trans_spherical_to_grid(Dyn%Spec%vor(:,:,future), Dyn%Grid%vor(:,:,future)) +call trans_spherical_to_grid(Dyn%Spec%div(:,:,future), Dyn%Grid%div(:,:,future)) + + +call uv_grid_from_vor_div (Dyn%Spec%vor (:,:,future), Dyn%Spec%div(:,:,future), & + Dyn%Grid%u (:,:,future), Dyn%Grid%v (:,:,future)) + +call trans_spherical_to_grid (Dyn%Spec%h (:,:,future), Dyn%Grid%h(:,:,future)) + +if(minval(Dyn%Grid%v) < valid_range_v(1) .or. maxval(Dyn%Grid%v) > valid_range_v(2)) then + call error_mesg('shallow_dynamics','meridional wind out of valid range', FATAL) +endif + +if(Dyn%spec_tracer) call update_spec_tracer(Dyn%Spec%trs, Dyn%Grid%trs, Dyn%Tend%trs, & + Dyn%Grid%u, Dyn%Grid%v, previous, current, future, delta_t) + +if(Dyn%grid_tracer) call update_grid_tracer(Dyn%Grid%tr, Dyn%Tend%tr, & + Dyn%Grid%u, Dyn%Grid%v, previous, current, future, delta_t) + + +! for diagnostics + +stream = compute_laplacian(Dyn%Spec%vor(:,:,current), -1) ! for diagnostic purposes +call trans_spherical_to_grid(stream, Dyn%grid%stream) + +Dyn%Grid%pv = vorg/(Dyn%Grid%h(:,:,current)) + +return +end subroutine shallow_dynamics +!================================================================================ + +subroutine implicit_correction(dt_divs, dt_hs, divs, hs, delta_t, previous, current) + +complex, intent(inout), dimension(ms:,ns:) :: dt_divs, dt_hs +complex, intent(in), dimension(ms:,ns:,:) :: divs, hs +real , intent(in) :: delta_t +integer, intent(in) :: previous, current + +real :: xi, mu, mu2 + +xi = 0.5 ! centered implicit (for backwards implicit, set xi = 1.0) + +mu = xi*delta_t +mu2 = mu*mu + +dt_hs = dt_hs + h_0*(divs(:,:,current) - divs(:,:,previous)) +dt_divs = dt_divs - eigen*(hs(:,:,current) - hs(:,:,previous)) + +dt_divs = (dt_divs + mu*eigen*dt_hs)/(1.0 + mu2*eigen*h_0) +dt_hs = dt_hs - mu*h_0*dt_divs + +return +end subroutine implicit_correction + +!=================================================================================== + +subroutine update_spec_tracer(tr_spec, tr_grid, dt_tr, ug, vg, & + previous, current, future, delta_t) + +complex, intent(inout), dimension(ms:me, ns:ne, num_time_levels) :: tr_spec +real , intent(inout), dimension(is:ie, js:je, num_time_levels) :: tr_grid +real , intent(inout), dimension(is:ie, js:je ) :: dt_tr +real , intent(in ), dimension(is:ie, js:je, num_time_levels) :: ug, vg +real , intent(in ) :: delta_t +integer, intent(in ) :: previous, current, future + +complex, dimension(ms:me, ns:ne) :: dt_trs + +call horizontal_advection (tr_spec(:,:,current), ug(:,:,current), vg(:,:,current), dt_tr) +call trans_grid_to_spherical (dt_tr, dt_trs) +call compute_spectral_damping (tr_spec(:,:,previous), dt_trs, delta_t) +call leapfrog (tr_spec, dt_trs, previous, current, future, delta_t, robert_coeff, raw_filter_coeff) +call trans_spherical_to_grid (tr_spec(:,:,future), tr_grid(:,:,future)) + +return +end subroutine update_spec_tracer +!========================================================================== + +subroutine update_grid_tracer(tr_grid, dt_tr_grid, ug, vg, & + previous, current, future, delta_t) + +real , intent(inout), dimension(is:ie, js:je, num_time_levels) :: tr_grid +real , intent(inout), dimension(is:ie, js:je ) :: dt_tr_grid +real , intent(in ), dimension(is:ie, js:je, num_time_levels) :: ug, vg + +real , intent(in ) :: delta_t +integer, intent(in ) :: previous, current, future + +real, dimension(is:ie,js:je) :: tr_current, tr_future + +tr_future = tr_grid(:,:,previous) + delta_t*dt_tr_grid +dt_tr_grid = 0.0 +call a_grid_horiz_advection (ug(:,:,current), vg(:,:,current), tr_future, delta_t, dt_tr_grid) +tr_future = tr_future + delta_t*dt_tr_grid +tr_current = tr_grid(:,:,current) + & + robert_coeff_tracer*(tr_grid(:,:,previous) + tr_future - 2.0*tr_grid(:,:,current)) +tr_grid(:,:,current) = tr_current +tr_grid(:,:,future) = tr_future + +return +end subroutine update_grid_tracer + +!========================================================================== + +subroutine read_restart(Dyn) + +type(dynamics_type), intent(inout) :: Dyn + +integer :: unit, m, n, nt +real, dimension(ms:me, ns:ne) :: real_part, imag_part + +if(file_exist('INPUT/shallow_dynamics.res.nc')) then + do nt = 1, 2 + call read_data('INPUT/shallow_dynamics.res.nc', 'vors_real', real_part, spectral_domain, timelevel=nt) + call read_data('INPUT/shallow_dynamics.res.nc', 'vors_imag', imag_part, spectral_domain, timelevel=nt) + do n=ns,ne + do m=ms,me + Dyn%Spec%vor(m,n,nt) = cmplx(real_part(m,n),imag_part(m,n)) + end do + end do + call read_data('INPUT/shallow_dynamics.res.nc', 'divs_real', real_part, spectral_domain, timelevel=nt) + call read_data('INPUT/shallow_dynamics.res.nc', 'divs_imag', imag_part, spectral_domain, timelevel=nt) + do n=ns,ne + do m=ms,me + Dyn%Spec%div(m,n,nt) = cmplx(real_part(m,n),imag_part(m,n)) + end do + end do + call read_data('INPUT/shallow_dynamics.res.nc', 'hs_real', real_part, spectral_domain, timelevel=nt) + call read_data('INPUT/shallow_dynamics.res.nc', 'hs_imag', imag_part, spectral_domain, timelevel=nt) + do n=ns,ne + do m=ms,me + Dyn%Spec%h(m,n,nt) = cmplx(real_part(m,n),imag_part(m,n)) + end do + end do + if(Dyn%spec_tracer) then + call read_data('INPUT/shallow_dynamics.res.nc', 'trs_real', real_part, spectral_domain, timelevel=nt) + call read_data('INPUT/shallow_dynamics.res.nc', 'trs_imag', imag_part, spectral_domain, timelevel=nt) + do n=ns,ne + do m=ms,me + Dyn%Spec%trs(m,n,nt) = cmplx(real_part(m,n),imag_part(m,n)) + end do + end do + endif + call read_data('INPUT/shallow_dynamics.res.nc', 'u', Dyn%Grid%u (:,:,nt), grid_domain, timelevel=nt) + call read_data('INPUT/shallow_dynamics.res.nc', 'v', Dyn%Grid%v (:,:,nt), grid_domain, timelevel=nt) + call read_data('INPUT/shallow_dynamics.res.nc', 'vor', Dyn%Grid%vor(:,:,nt), grid_domain, timelevel=nt) + call read_data('INPUT/shallow_dynamics.res.nc', 'div', Dyn%Grid%div(:,:,nt), grid_domain, timelevel=nt) + call read_data('INPUT/shallow_dynamics.res.nc', 'h', Dyn%Grid%h (:,:,nt), grid_domain, timelevel=nt) + if(Dyn%spec_tracer) then + call read_data('INPUT/shallow_dynamics.res.nc', 'trs', Dyn%Grid%trs(:,:,nt), grid_domain, timelevel=nt) + endif + if(Dyn%grid_tracer) then + call read_data('INPUT/shallow_dynamics.res.nc', 'tr', Dyn%Grid%tr(:,:,nt), grid_domain, timelevel=nt) + endif + end do +else if(file_exist('INPUT/shallow_dynamics.res')) then + unit = open_restart_file(file='INPUT/shallow_dynamics.res',action='read') + + do nt = 1, 2 + call set_domain(spectral_domain) + call read_data(unit,Dyn%Spec%vor(:,:, nt)) + call read_data(unit,Dyn%Spec%div(:,:, nt)) + call read_data(unit,Dyn%Spec%h (:,:, nt)) + if(Dyn%spec_tracer) call read_data(unit,Dyn%Spec%trs(:,:, nt)) + + call set_domain(grid_domain) + call read_data(unit,Dyn%Grid%u (:,:, nt)) + call read_data(unit,Dyn%Grid%v (:,:, nt)) + call read_data(unit,Dyn%Grid%vor (:,:, nt)) + call read_data(unit,Dyn%Grid%div (:,:, nt)) + call read_data(unit,Dyn%Grid%h (:,:, nt)) + if(Dyn%spec_tracer) call read_data(unit,Dyn%Grid%trs(:,:, nt)) + if(Dyn%grid_tracer) call read_data(unit,Dyn%Grid%tr (:,:, nt)) + + end do + call close_file(unit) + +else + call error_mesg('read_restart', 'restart does not exist', FATAL) +endif + +return +end subroutine read_restart + +!==================================================================== + +subroutine write_restart(Dyn, previous, current) + +type(dynamics_type), intent(in) :: Dyn +integer, intent(in) :: previous, current + +integer :: unit, nt, nn + +do nt = 1, 2 + if(nt == 1) nn = previous + if(nt == 2) nn = current + call write_data('RESTART/shallow_dynamics.res.nc', 'vors_real', real(Dyn%Spec%vor(:,:,nn)), spectral_domain) + call write_data('RESTART/shallow_dynamics.res.nc', 'vors_imag', aimag(Dyn%Spec%vor(:,:,nn)), spectral_domain) + call write_data('RESTART/shallow_dynamics.res.nc', 'divs_real', real(Dyn%Spec%div(:,:,nn)), spectral_domain) + call write_data('RESTART/shallow_dynamics.res.nc', 'divs_imag', aimag(Dyn%Spec%div(:,:,nn)), spectral_domain) + call write_data('RESTART/shallow_dynamics.res.nc', 'hs_real', real(Dyn%Spec%h (:,:,nn)), spectral_domain) + call write_data('RESTART/shallow_dynamics.res.nc', 'hs_imag', aimag(Dyn%Spec%h (:,:,nn)), spectral_domain) + if(Dyn%spec_tracer) then + call write_data('RESTART/shallow_dynamics.res.nc', 'trs_real', real(Dyn%Spec%trs(:,:,nn)), spectral_domain) + call write_data('RESTART/shallow_dynamics.res.nc', 'trs_imag', aimag(Dyn%Spec%trs(:,:,nn)), spectral_domain) + endif + call write_data('RESTART/shallow_dynamics.res.nc', 'u', Dyn%Grid%u (:,:,nn), grid_domain) + call write_data('RESTART/shallow_dynamics.res.nc', 'v', Dyn%Grid%v (:,:,nn), grid_domain) + call write_data('RESTART/shallow_dynamics.res.nc', 'vor', Dyn%Grid%vor(:,:,nn), grid_domain) + call write_data('RESTART/shallow_dynamics.res.nc', 'div', Dyn%Grid%div(:,:,nn), grid_domain) + call write_data('RESTART/shallow_dynamics.res.nc', 'h', Dyn%Grid%h (:,:,nn), grid_domain) + if(Dyn%spec_tracer) then + call write_data('RESTART/shallow_dynamics.res.nc', 'trs', Dyn%Grid%trs(:,:,nn), grid_domain) + endif + if(Dyn%grid_tracer) then + call write_data('RESTART/shallow_dynamics.res.nc', 'tr', Dyn%Grid%tr(:,:,nn), grid_domain) + endif +enddo + +!unit = open_restart_file(file='RESTART/shallow_dynamics.res', action='write') + +!do n = 1, 2 +! if(n == 1) nn = previous +! if(n == 2) nn = current +! +! call set_domain(spectral_domain) +! call write_data(unit,Dyn%Spec%vor(:,:, nn)) +! call write_data(unit,Dyn%Spec%div(:,:, nn)) +! call write_data(unit,Dyn%Spec%h (:,:, nn)) +! if(Dyn%spec_tracer) call write_data(unit,Dyn%Spec%trs(:,:, nn)) +! +! call set_domain(grid_domain) +! call write_data(unit,Dyn%Grid%u (:,:, nn)) +! call write_data(unit,Dyn%Grid%v (:,:, nn)) +! call write_data(unit,Dyn%Grid%vor (:,:, nn)) +! call write_data(unit,Dyn%Grid%div (:,:, nn)) +! call write_data(unit,Dyn%Grid%h (:,:, nn)) +! if(Dyn%spec_tracer) call write_data(unit,Dyn%Grid%trs(:,:, nn)) +! if(Dyn%grid_tracer) call write_data(unit,Dyn%Grid%tr (:,:, nn)) +! +!end do + +!call close_file(unit) + +end subroutine write_restart + +!==================================================================== + +subroutine shallow_dynamics_end (Dyn, previous, current) + +type(dynamics_type), intent(inout) :: Dyn +integer, intent(in) :: previous, current + +if(.not.module_is_initialized) then + call error_mesg('shallow_dynamics_end','dynamics has not been initialized ', FATAL) +endif + +call write_restart (Dyn, previous, current) + +call transforms_end +call stirring_end + +module_is_initialized = .false. + +return +end subroutine shallow_dynamics_end +!=================================================================================== + +end module shallow_dynamics_mod diff --git a/src/atmos_spectral_shallow/shallow_dynamics.html b/src/atmos_spectral_shallow/shallow_dynamics.html new file mode 100644 index 000000000..600933a30 --- /dev/null +++ b/src/atmos_spectral_shallow/shallow_dynamics.html @@ -0,0 +1,370 @@ + +module shallow_dynamics_mod + + +
+ + +

module shallow_dynamics_mod

+ + +
+     Contact: Isaac Held
+     Reviewers: Peter Phillipps
+
+
+
+ + +
+

OVERVIEW

+ +
+
+   The dynamical core of the spectral transform model for 
+   the shallow water equations on the sphere.  
+   
+
+
+ + +
+

DESCRIPTION

+ +
+
+   Integrates the shallow water equation for hydrostatic flow of a homgeoneous,
+   incompressible fluid on the
+   sphere using the spectral transform technique.  Also allows for the
+   inclusion of a passive tracer advected by the the spectral advection
+   algorithm, and a gridpoint tracer advected with a finite
+   volume  algorithm on the transform grid.  Thinking of the model as one of
+   the upper tropopsheric flow, the default experiment involves relaxation of 
+   the geopotential to an "equilibrium value" with maxima (whose amplitude
+   and shape are controlled from the namelist) along the equator and in the 
+   subtropicals.
+
+   For a full description of the model and algorithms used, see 
+     shallow.ps 
+   
+   For higher level routines for running this shallow water model,
+   see  atmosphere_mod 
+
+
+
+ + + +
+

OTHER MODULES USED

+ +
+
+     fms_mod
+     constants_mod
+     time_manager_mod
+     transforms_mod
+     spectral_damping_mod
+     leapfrog_mod
+     fv_advection_mod
+
+
+
+ + +
+

PUBLIC INTERFACE

+ +
+
+  use shallow_dynamics_mod [,only: shallow_dynamics_init,       
+                                   shallow_dynamics,
+			           shallow_dynamics_end,
+                                   dynamics_type,
+				   grid_type,
+				   spectral_type,
+				   tendency_type]
+                                
+
+
+ + +
+

PUBLIC DATA

+ +
+     
+
+
+ +type grid_type + real, pointer, dimension(:,:,:) :: u, v, vor, div, h, trs, tr + real, pointer, dimension(:,:) :: stream, pv +end type + + allocated space for grid fields + + (:,:,:) => (lon, lat, time_level) + (:,:) => (lon, lat) + (lon, lat) on local computational domain + time_level stores the two time levels needed for the + leapfrog step + + u -- eastward velocity (m/s) + v -- northward velocity (m/s) + vor -- vorticity (1/s) + div -- divergence (1/s) + h -- geopotential (m^2/s^2) + trs -- tracer advected spectrally + tr -- tracer advected on grid + pv -- (f + vor)/h, where f = 2*omega*sin(lat) (s/m^2) + stream -- streamfunction (m^2/s) at current time + + + +
+ + +type spectral_type + complex, pointer, dimension(:,:,:) :: vor, div, h, trs +end type + + allocated space for spectral fields + + (:,:,:) => (zonal, meridional, time_level) + + vor -- spectral vorticity + div -- spectral divergence + h -- spectral geopotential + trs -- spectral tracer + +
+
+ +type tendency_type + real, pointer, dimension(:,:) :: u, v, h, trs, tr +end type + + allocated space for accumulating tendencies, d/dt, in grid space, + for prognostic variables + + (:,:,:) => (lon, lat) + +
+
+ +type dynamics_type + type(grid_type) :: grid + type(spectral_type) :: spec + type(tendency_type) :: tend + integer :: num_lon, num_lat ! size of global domain + logical :: grid_tracer, spec_tracer +end type + + grid_tracer = .true. => tracer with gridpoint advection is beign integrated + similarly for spec_tracer + +
+ +
+ + + +
+

PUBLIC ROUTINES

+ +
+
+subroutine  shallow_dynamics_init
+subroutine  shallow _dynamics
+subroutine  shallow_dynamics_end
+type (grid_type)
+type (spectral_type)
+type (tendency_type)
+type (dynamics_type)
+
+
+
+
+
+ subroutine shallow_dynamics_init(Dyn,  Time, Time_init)
+ 
+   type(dynamics_type), intent(inout)  :: Dyn
+         type containing all dynamical fields and related information
+	 (see type (dynamics_type))
+	 
+   type(time_type)    , intent(in)     :: Time, Time_init
+         current time and time at which integeration began
+	 time_type defined by time_manager_mod
+         
+
+   Initializes the module;
+   Reads restart from 'INPUT/shallow_dynamics.res' if Time = Time_init;
+     otherwise uses default initial conditions
+
+
+
+
+ + + + subroutine shallow_dynamics & + (Time, Time_init, Dyn, previous, current, future, delta_t) + + type(time_type) , intent(inout) :: Time, Time_init + type(dynamics_type), intent(inout) :: Dyn + integer , intent(in ) :: previous, current, future + real , intent(in ) :: delta_t + + previous, current and future = 1 or 2 + these integers refer to the third dimension of the + three-dimensional fields in Dyn + the fields at time t - delta_t are assumed to be in (:,:,previous) + the fields at time t are assumed to be in (:,:,current) + the fields at time t + delta_t are placed in (:,:,future) + overwriting whatever is already there + + delta_t = time step in seconds + + updates dynamical fields by one time step + + + +
+ + + subroutine shallow_dynamics_end(Dyn, previous, current) + + type(dynamics_type), intent(inout) :: Dyn + integer, intent(in) :: previous, current + + + Terminates module; + writes restart file to 'RESTART/shallow_dynamics.res' + + + + +
+
+ + + +
+

NAMELIST

+ +
+
+&shallow_dynamics_nml
+
+  integer :: num_lat            = 128  
+        number of latitudes in global grid
+       
+  integer :: num_lon            = 256
+        number of longitudes in global grid
+        should equal 2*num_lat for Triangular truncation
+  
+  integer :: num_fourier        = 85
+        the retained fourier wavenumber are n*fourier_inc, where
+        n ranges from 0 to num_fourier
+	 
+  integer :: num_spherical      = 86
+        the maximum number of meridional modes for any zonal wavenumber
+        for triangular truncation, set num_spherical = num_fourier +1
+         
+  integer :: fourier_inc        = 1
+        creates a "sector" model if fourier_inc > 1; integration domain is
+	(360 degrees longitude)/fourier_inc
+	
+  (the default values listed above define a standard T85 model)
+
+  logical :: check_fourier_imag = .false.
+        if true, checks to see if fields to be transformed to grid 
+	domain have zero imaginary part to their zonally symmetric
+	modes; useful for debugging
+	
+  logical :: south_to_north     = .true.
+        true => grid runs from south to north
+	false => grid runs from north to south
+	
+  logical :: triangular_trunc   = .true.
+        true  => shape of truncation is triangular
+	false => shape of truncation is rhomboidal
+
+  real    :: robert_coeff       = 0.04
+        x(current) => (1-2r)*x(current) + r*(x(future)+x(previous))
+	where r = robert_coeff (non-dimensional)
+	
+  real    :: robert_coeff_tracer       = 0.04
+        (same as robert_coeff, but for grid tracer)
+	
+  real    :: longitude_origin   = 0.0
+        longitude of first longitude, in degrees
+	(if you want the westgern boundary of first grid boc to be at 
+         0.0, set longitude_origin = 0.5*360./float(num_lon))
+	 
+  integer :: damping_option     = 'resolution_dependent'
+  integer :: damping_order      = 4
+  real    :: damping_coeff      = 1.e-04
+  
+        damping = nu*(del^2)^n where n = damping order
+	damping_option = 'resolution_dependent' or 'resolution_independent'
+	  = 'resolution_dependent' => nu is set so that the damping rate for the 
+	        mode (m=0,n=num_spherical-1) equals damping_coeff (in 1/s)
+	        For triangular truncation, damping_coeff is then the 
+	        rate of damping of the highest retained mode
+	     
+	  = 'resolution_independent' => nu = damping_coef
+	  
+  real    :: h_0  = 3.e04
+          (m^2)/(s^2) 
+          the initial condition is a state of rest with geopotential = h_0
+	  (h_0 is also used to determine the part of the divergence equation
+	   that is integrated implicitly)
+	
+  logical :: spec_tracer      = .true.
+  logical :: grid_tracer      = .true.
+       spec_tracer = true => a passive tracer is carried that is advected
+          spectrally, with the same algorithm as the vorticity
+       grid_tracer = ture => a passive tracer is carried that is advected
+          on the spectral transform grid by a finite-volume algorithm
+	  (see  shallow.ps )
+       Both tracers can be carried simultaeneously
+	  
+  real, dimension(2) :: valid_range_v = -1000., 1000.
+        A valid range for meridional wind. Model terminates if meridional wind
+	goes outside the valid range. Allows model to terminate gracefully when,
+	for example, the model becomes numerically unstable.
+
+
+ + + +
+

ERROR MESSAGES

+ +
+
+   "Dynamics has not been initialized"
+      -- shallow_dynamics_init must be called before any other
+         routines in the module are called
+	 
+   "restart does not exist" 
+      -- Time is not equal to Time_init at initalization, but the file
+          'INPUT/shallow_dynamics.res' does not exit 
+	 
+
+
+
+ + +
+ + diff --git a/src/atmos_spectral_shallow/shallow_physics.F90 b/src/atmos_spectral_shallow/shallow_physics.F90 new file mode 100644 index 000000000..8f8896d2e --- /dev/null +++ b/src/atmos_spectral_shallow/shallow_physics.F90 @@ -0,0 +1,232 @@ +module shallow_physics_mod + +!----------------------------------------------------------------------- +! GNU General Public License +! +! This program is free software; you can redistribute it and/or modify it and +! are expected to follow the terms of the GNU General Public License +! as published by the Free Software Foundation; either version 2 of +! the License, or (at your option) any later version. +! +! This program is distributed in the hope that it will be useful, but WITHOUT +! ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +! or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +! License for more details. +! +! For the full text of the GNU General Public License, +! write to: Free Software Foundation, Inc., +! 675 Mass Ave, Cambridge, MA 02139, USA. +! or see: http://www.gnu.org/licenses/gpl.html +!----------------------------------------------------------------------- + +use fms_mod, only: open_namelist_file, & + open_restart_file, & + file_exist, & + check_nml_error, & + error_mesg, & + FATAL, WARNING, & + write_version_number, & + mpp_pe, & + mpp_root_pe, & + fms_init, fms_end, & + read_data, & + write_data, & + set_domain, & + close_file, & + stdlog + +use transforms_mod, only: get_sin_lat, get_cos_lat, & + get_deg_lon, get_deg_lat, & + get_wts_lat, & + get_grid_domain, get_spec_domain, & + grid_domain + +use time_manager_mod, only: time_type + +!======================================================================== +implicit none +private +!======================================================================== + +public :: shallow_physics_init, & + shallow_physics, & + shallow_physics_end, & + phys_type + + +! version information +!======================================================================== +character(len=128) :: version = '$Id: shallow_physics.F90,v 10.0 2003/10/24 22:01:02 fms Exp $' +character(len=128) :: tagname = '$Name: siena_201207 $' +!======================================================================== + +type phys_type + real, pointer, dimension(:,:) :: empty=>NULL() +end type + +logical :: module_is_initialized = .false. + +integer :: is, ie, js, je + +integer :: pe +logical :: root + +real, allocatable, dimension(:) :: rad_lat, deg_lat, deg_lon, & + sin_lat, cos_lat, wts_lat + +real, allocatable, dimension(:,:) :: h_eq + +real :: kappa_m, kappa_t + + + +! namelist +!======================================================================== + +real :: fric_damp_time = -20.0 +real :: therm_damp_time = -10.0 +real :: del_h = 1.e04 +real :: h_0 = 3.e04 +real :: h_amp = 2.e04 +real :: h_lon = 90.0 +real :: h_lat = 25.0 +real :: h_width = 15.0 +real :: h_itcz = 1.e05 +real :: itcz_width = 4.0 + +namelist /shallow_physics_nml/ fric_damp_time, therm_damp_time, del_h, h_0, & + h_amp, h_lon, h_lat, h_width, & + itcz_width, h_itcz +!======================================================================== + +contains + +!======================================================================== + +subroutine shallow_physics_init(Phys) + +type(phys_type), intent(inout) :: Phys + +integer :: i, j, unit, ierr, io + +real :: xx, yy, dd + +call write_version_number(version, tagname) + +pe = mpp_pe() +root = (pe == mpp_root_pe()) + +! read the namelist + +if (file_exist('input.nml')) then + unit = open_namelist_file () + ierr=1 + do while (ierr /= 0) + read (unit, nml=shallow_physics_nml, iostat=io, end=10) + ierr = check_nml_error (io, 'shallow_physics_nml') + enddo + 10 call close_file (unit) +endif + +if(fric_damp_time < 0.0) fric_damp_time = - fric_damp_time*86400 +if(therm_damp_time < 0.0) therm_damp_time = - therm_damp_time*86400 + +kappa_m = 0.0 +kappa_t = 0.0 +if( fric_damp_time .ne. 0.0) kappa_m = 1./fric_damp_time +if(therm_damp_time .ne. 0.0) kappa_t = 1./therm_damp_time + +call get_grid_domain(is,ie,js,je) + +allocate ( rad_lat (js:je) ) +allocate ( deg_lat (js:je) ) +allocate ( sin_lat (js:je) ) +allocate ( cos_lat (js:je) ) +allocate ( wts_lat (js:je) ) +allocate ( deg_lon (is:ie) ) +allocate ( h_eq (is:ie,js:je) ) + +call get_wts_lat(wts_lat) +call get_deg_lat(deg_lat) +call get_deg_lon(deg_lon) +rad_lat = deg_lat*atan(1.)/45. +sin_lat = sin(rad_lat) +cos_lat = cos(rad_lat) + + +do j = js, je + do i = is, ie + xx = (deg_lon(i) - h_lon)/(h_width*2.0) + yy = (deg_lat(j) - h_lat)/h_width + dd = xx*xx + yy*yy + h_eq(i,j) = h_0 + h_amp*max(1.e-10, exp(-dd)) + end do +end do + +do j = js, je + yy = deg_lat(j)/itcz_width + dd = yy*yy + h_eq(:,j) = h_eq(:,j) + h_itcz*exp(-dd) +end do + +!if(file_exist('INPUT/shallow_physics.res')) then +! unit = open_restart_file(file='INPUT/shallow_physics.res',action='read') +! call set_domain(grid_domain) +! call close_file(unit) +!else + +!endif + +module_is_initialized = .true. + +return +end subroutine shallow_physics_init + +!======================================================================= + +subroutine shallow_physics(Time, dt_ug, dt_vg, dt_hg, ug, vg, hg, & + delta_t, previous, current, Phys) + +real, intent(inout), dimension(is:ie, js:je) :: dt_ug, dt_vg, dt_hg +real, intent(in) , dimension(is:ie, js:je, 2) :: ug, vg, hg + +real , intent(in) :: delta_t +integer, intent(in) :: previous, current + +type(time_type), intent(in) :: Time +type(phys_type), intent(inout) :: Phys + +dt_ug = dt_ug - kappa_m*ug(:,:,previous) +dt_vg = dt_vg - kappa_m*vg(:,:,previous) +dt_hg = dt_hg - kappa_t*(hg(:,:,previous) - h_eq) + + +return +end subroutine shallow_physics + +!====================================================================== + +subroutine shallow_physics_end(Phys) + +type(phys_type), intent(in) :: Phys + +integer :: unit + +if(.not.module_is_initialized) then + call error_mesg('shallow_physics_end','physics has not been initialized ', FATAL) +endif + +!unit = open_restart_file(file='RESTART/shallow_physics.res', action='write') + +!call set_domain(grid_domain) + +!call close_file(unit) + +module_is_initialized = .false. + +return +end subroutine shallow_physics_end + +!====================================================================== + +end module shallow_physics_mod diff --git a/src/atmos_spectral_shallow/shallow_physics.html b/src/atmos_spectral_shallow/shallow_physics.html new file mode 100644 index 000000000..d3ad34980 --- /dev/null +++ b/src/atmos_spectral_shallow/shallow_physics.html @@ -0,0 +1,210 @@ + +module shallow_physics_mod + + +
+ + +

module shallow_physics_mod

+ + +
+     Contact: Isaac Held
+     Reviewers: Peter Phillipps
+
+
+
+ + +
+

OVERVIEW

+ +
+
+   A module that allows one to add processes that act in the grid domain
+   to the dynamics of the shallow model on the sphere
+   
+
+
+ + +
+

DESCRIPTION

+ +
+
+   A module that allows one to add processes that act in the grid domain
+   to the dynamics of the shallow model on the sphere.  Currently adds
+   a relaxation to a specified  "equilibrium geopotential" and relaxes
+   the winds to zero
+
+
+ + +
+

OTHER MODULES USED

+ +
+
+     fms_mod
+     transforms_mod
+     time_manager_mod
+
+
+
+ + +
+

PUBLIC INTERFACE

+ +
+
+  use shallow_physics_mod [,only: shallow_physics_init,       
+                                         shallow_physics,
+					 shallow_physics_end,
+					 phys_type]
+                                
+
+
+ + +
+

PUBLIC DATA

+ +
+     
+
+
+ +type phys_type + real, pointer, dimension(:,:) :: empty +end type + + fields from physics module made available for diagnostics + +
+
+ +
+ + + +
+

PUBLIC ROUTINES

+ +
+
+subroutine  shallow_physics_init
+subroutine  shallow_physics
+subroutine  shallow_physics_end
+type(phys_type)
+
+
+
+
+
+ subroutine shallow_physics_init(Phys)
+ 
+   type(phys_type)    , intent(inout)     :: Phys
+  
+ 
+   Initializes module
+
+
+
+
+ + + + subroutine shallow_physics (Time, dt_ug, dt_vg, dt_hg, ug, vg, hg, & + delta_t, previous, current, Phys) + + real, intent(inout), dimension(:,:) :: dt_ug, dt_vg, dt_hg + + the u, v and geopotential tendencies onto which tendencies due to + the grid-point physics are added (m/(s^2) for dt_ug, dt_vg; + (m^2)/(s^3) for dt_hg) + + real, intent(in) , dimension(:,:, 2) :: ug, vg, hg + the grid zonal and meridional velocities (m/s) and + geopotential (m^2/s^2) + the third index is the time-index used in the leapfrog step + + real , intent(in) :: delta_t + time step (s) + + integer, intent(in) :: previous, current + = 1 or 2 + ug(:,:,previous) is the velocity at t-delta_t + ug(:,:,current ) is the velocity at t + + type(time_type), intent(in) :: Time + type(phys_type), intent(inout) :: Phys + + + + +
+ + + + subroutine shallow_physics_end (Phys) + + type(phys_type), intent(inout) :: Phys + + + +
+
+ + + + +
+

NAMELIST

+ +
+
+&shallow_physics_nml
+
+real    :: fric_damp_time  = -20.0
+           rate at which ua nd v are relaxed to zero (seconds)
+           (if negative, units are days instead -- negative sign is ignored) 
+      
+real    :: therm_damp_time = -10.0
+           rate at which geopotential is relaxed to h_eq
+	   (units as above)
+
+real    :: h_0             = 3.e04  (m^2/s^2)
+real    :: h_amp           = 2.e04  (m^2/s^2)
+real    :: h_lon           =  90.0  degrees
+real    :: h_lat           =  25.0  degrees
+real    :: h_width         =  15.0  degrees
+real    :: h_itcz          = 1.e05  (m^2/s^2)
+real    :: itcz_width      =  4.0   degrees
+
+           h_eq is defined as
+	   h_0 + h_amp*exp(-r^2) + h_itcz*exp(-d^2)
+	   
+	   where r^2 = xx^2 + yy^2
+	      xx = (lon - h_lon)/(2*h_width)
+	      yy = (lat - h_lat)/h_width
+	      
+	   and d = lat/itcz_width
+	  
+
+
+
+ + +
+ + + diff --git a/src/coupler/surface_flux.F90 b/src/coupler/surface_flux.F90 index 97a207f51..d38850528 100644 --- a/src/coupler/surface_flux.F90 +++ b/src/coupler/surface_flux.F90 @@ -261,11 +261,11 @@ module surface_flux_mod logical :: raoult_sat_vap = .false. logical :: do_simple = .false. -real :: land_humidity_prefactor = 1.0 !s Default is that land makes no difference to evaporative fluxes -real :: land_evap_prefactor = 1.0 !s Default is that land makes no difference to evaporative fluxes +real :: land_humidity_prefactor = 1.0 ! Default is that land makes no difference to evaporative fluxes +real :: land_evap_prefactor = 1.0 ! Default is that land makes no difference to evaporative fluxes -real :: flux_heat_gp = 5.7 !s Default value for Jupiter of 5.7 Wm^-2 -real :: diabatic_acce = 1.0 !s Diabatic acceleration?? +real :: flux_heat_gp = 5.7 ! Default value for Jupiter of 5.7 Wm^-2 +real :: diabatic_acce = 1.0 ! Diabatic acceleration?? namelist /surface_flux_nml/ no_neg_q, & @@ -279,10 +279,10 @@ module surface_flux_mod ncar_ocean_flux_orig, & raoult_sat_vap, & do_simple, & - land_humidity_prefactor, & !s Added to make land 'dry', i.e. to decrease the evaporative heat flux in areas of land. - land_evap_prefactor, & !s Added to make land 'dry', i.e. to decrease the evaporative heat flux in areas of land. - flux_heat_gp, & !s prescribed lower boundary heat flux on a giant planet - diabatic_acce + land_humidity_prefactor, & ! Added to make land 'dry', i.e. to decrease the evaporative heat flux in areas of land. + land_evap_prefactor, & ! Added to make land 'dry', i.e. to decrease the evaporative heat flux in areas of land. + flux_heat_gp, & ! prescribed lower boundary heat flux on a giant planet + diabatic_acce @@ -338,8 +338,8 @@ module surface_flux_mod subroutine surface_flux_1d ( & t_atm, q_atm_in, u_atm, v_atm, p_atm, z_atm, & p_surf, t_surf, t_ca, q_surf, & - bucket, bucket_depth, max_bucket_depth_land, & !RG Add bucket - depth_change_lh_1d, depth_change_conv_1d, depth_change_cond_1d, & !RG Add bucket + bucket, bucket_depth, max_bucket_depth_land, & + depth_change_lh_1d, depth_change_conv_1d, depth_change_cond_1d, & u_surf, v_surf, & rough_mom, rough_heat, rough_moist, rough_scale, gust, & flux_t, flux_q, flux_r, flux_u, flux_v, & @@ -347,16 +347,16 @@ subroutine surface_flux_1d ( & w_atm, u_star, b_star, q_star, & dhdt_surf, dedt_surf, dedq_surf, drdt_surf, & dhdt_atm, dedq_atm, dtaudu_atm, dtaudv_atm, & - ex_del_m, ex_del_h, ex_del_q, & !mp586 for 10m winds and 2m temp - temp_2m, u_10m, v_10m, & !mp586 for 10m winds and 2m temp - q_2m, rh_2m, & !Add 2m q and RH + ex_del_m, ex_del_h, ex_del_q, & + temp_2m, u_10m, v_10m, & + q_2m, rh_2m, & dt, land, seawater, avail ) ! ! slm Mar 28 2002 -- remove agument drag_q since it is just cd_q*wind ! ============================================================================ ! ---- arguments ----------------------------------------------------------- logical, intent(in), dimension(:) :: land, seawater, avail - logical, intent(in) :: bucket !RG Add bucket + logical, intent(in) :: bucket ! Add bucket model real, intent(in), dimension(:) :: & t_atm, q_atm_in, u_atm, v_atm, & p_atm, z_atm, t_ca, & @@ -368,22 +368,22 @@ subroutine surface_flux_1d ( & dhdt_atm, dedq_atm, dtaudu_atm,dtaudv_atm, & w_atm, u_star, b_star, q_star, & cd_m, cd_t, cd_q, & - ex_del_m, ex_del_h, ex_del_q, & !mp586 for 10m winds and 2m temp - temp_2m, u_10m, v_10m, & !mp586 for 10m winds and 2m temp - q_2m, rh_2m ! Add 2m q and RH + ex_del_m, ex_del_h, ex_del_q, & + temp_2m, u_10m, v_10m, & + q_2m, rh_2m real, intent(inout), dimension(:) :: q_surf - real, intent(inout), dimension(:) :: bucket_depth !RG Add bucket - real, intent(inout), dimension(:) :: depth_change_lh_1d !RG Add bucket - real, intent(in), dimension(:) :: depth_change_conv_1d, depth_change_cond_1d !RG Add bucket + real, intent(inout), dimension(:) :: bucket_depth + real, intent(inout), dimension(:) :: depth_change_lh_1d + real, intent(in), dimension(:) :: depth_change_conv_1d, depth_change_cond_1d real, intent(in) :: max_bucket_depth_land real, intent(in) :: dt ! ---- local constants ----------------------------------------------------- ! temperature increment and its reciprocal value for comp. of derivatives real, parameter:: del_temp=0.1, del_temp_inv=1.0/del_temp - real:: zrefm, zrefh !mp586 for 10m winds and 2m temp + real:: zrefm, zrefh ! ---- local vars ---------------------------------------------------------- @@ -432,7 +432,7 @@ subroutine surface_flux_1d ( & ! initilaize surface air humidity according to surface type where (land) ! q_surf0 = q_surf ! land calculates it - q_surf0 = q_sat !s our simplified land evaporation model does not calculate q_surf, so we specify it as q_sat. + q_surf0 = q_sat ! our simplified land evaporation model does not calculate q_surf, so we specify it as q_sat. elsewhere q_surf0 = q_sat ! everything else assumes saturated sfc humidity endwhere @@ -505,9 +505,7 @@ subroutine surface_flux_1d ( & rough_mom, rough_heat, rough_moist, w_atm, & cd_m, cd_t, cd_q, u_star, b_star, avail ) -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!!!!!!! added by mp586 for 10m winds and 2m temperature add mo_profile()!!!!!!!! - +! added for 10m winds and 2m temperature add mo_profile() zrefm = 10. !want winds at 10m zrefh = 2. !want temp and q at 2m @@ -517,10 +515,8 @@ subroutine surface_flux_1d ( & u_star, b_star, q_star, & ex_del_m, ex_del_h, ex_del_q, avail ) - ! adapted from https://github.com/mom-ocean/MOM5/blob/3702ad86f9653f4e315b98613eb824a47d89cf00/src/coupler/flux_exchange.F90#L1932 - ! ------- reference temp ----------- where (avail) & temp_2m = t_surf + (t_atm - t_surf) * ex_del_h !t_ca = canopy temperature, assuming that there is no canopy (no difference between land and ocean), t_ca = t_surf @@ -533,8 +529,8 @@ subroutine surface_flux_1d ( & where (avail) & v_10m = v_atm * ex_del_m ! setting v at surface to 0. -!!!!!!!!!!!! end of mp586 additions !!!!!!!!!!!!!!!!!!!!!!! -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +! end of low level wind additions + ! Add 2m q and RH @@ -584,29 +580,29 @@ subroutine surface_flux_1d ( & ! evaporation rho_drag = drag_q * rho - end where + end where -!RG Add bucket - if bucket is on evaluate fluxes based on moisture availability. -!RG Note changes to avail statements to allow bucket to be switched on or off +! Add bucket - if bucket is on evaluate fluxes based on moisture availability. +! Note changes to avail statements to allow bucket to be switched on or off if (bucket) then where (avail) ! begin LJJ addition where(land) where (bucket_depth >= max_bucket_depth_land*0.75) flux_q = rho_drag * (q_surf0 - q_atm) - elsewhere + elsewhere flux_q = bucket_depth/(max_bucket_depth_land*0.75) * rho_drag * (q_surf0 - q_atm) ! flux of water vapor (Kg/(m**2 s)) end where elsewhere flux_q = rho_drag * (q_surf0 - q_atm) ! flux of water vapor (Kg/(m**2 s)) end where - - depth_change_lh_1d = flux_q * dt/dens_h2o + + depth_change_lh_1d = flux_q * dt/dens_h2o where (flux_q > 0.0 .and. bucket_depth < depth_change_lh_1d) ! where more evaporation than what's in bucket, empty bucket flux_q = bucket_depth * dens_h2o / dt depth_change_lh_1d = flux_q * dt / dens_h2o - end where - + end where + where (bucket_depth <= 0.0) dedt_surf = 0. dedq_surf = 0. @@ -623,19 +619,19 @@ subroutine surface_flux_1d ( & elsewhere dedt_surf = rho_drag * (q_sat1 - q_sat) *del_temp_inv end where - + end where - end where + end where else -!RG otherwise revert to simple land model +! otherwise revert to simple land model where (avail) where (land) -!s Simplified land model uses simple prefactor in front of qsurf0. Land is therefore basically the same as sea, but with this prefactor, hence the changes to dedq_surf and dedt_surf also. +! Simplified land model uses simple prefactor in front of qsurf0. Land is therefore basically the same as sea, but with this prefactor, hence the changes to dedq_surf and dedt_surf also. flux_q = rho_drag * land_evap_prefactor * (land_humidity_prefactor*q_surf0 - q_atm) ! flux of water vapor (Kg/(m**2 s)) dedq_surf = 0 dedt_surf = rho_drag * land_evap_prefactor * (land_humidity_prefactor*q_sat1 - q_sat) *del_temp_inv -! dedq_surf = rho_drag +! dedq_surf = rho_drag ! dedt_surf = 0 elsewhere flux_q = rho_drag * (q_surf0 - q_atm) ! flux of water vapor (Kg/(m**2 s)) @@ -646,7 +642,7 @@ subroutine surface_flux_1d ( & end where endif -!RG end Add bucket changes +! end of Add bucket changes where (avail) @@ -713,9 +709,9 @@ subroutine surface_flux_0d ( & w_atm_0, u_star_0, b_star_0, q_star_0, & dhdt_surf_0, dedt_surf_0, dedq_surf_0, drdt_surf_0, & dhdt_atm_0, dedq_atm_0, dtaudu_atm_0, dtaudv_atm_0, & - ex_del_m_0, ex_del_h_0, ex_del_q_0, & !mp586 for 10m winds and 2m temp - temp_2m_0, u_10m_0, v_10m_0, & !mp586 for 10m winds and 2m temp - q_2m_0, rh_2m_0, & !2m q and RH + ex_del_m_0, ex_del_h_0, ex_del_q_0, & + temp_2m_0, u_10m_0, v_10m_0, & + q_2m_0, rh_2m_0, & dt, land_0, seawater_0, avail_0 ) ! ---- arguments ----------------------------------------------------------- @@ -730,9 +726,9 @@ subroutine surface_flux_0d ( & dhdt_surf_0, dedt_surf_0, dedq_surf_0, drdt_surf_0, & dhdt_atm_0, dedq_atm_0, dtaudu_atm_0,dtaudv_atm_0, & w_atm_0, u_star_0, b_star_0, q_star_0, & - cd_m_0, cd_t_0, cd_q_0, & - ex_del_m_0, ex_del_h_0, ex_del_q_0, & !mp586 for 10m winds and 2m temp - temp_2m_0, u_10m_0, v_10m_0, & !mp586 for 10m winds and 2m temp + cd_m_0, cd_t_0, cd_q_0, & + ex_del_m_0, ex_del_h_0, ex_del_q_0, & + temp_2m_0, u_10m_0, v_10m_0, & q_2m_0, rh_2m_0 real, intent(inout) :: q_surf_0 real, intent(in) :: dt @@ -750,16 +746,16 @@ subroutine surface_flux_0d ( & dhdt_surf, dedt_surf, dedq_surf, drdt_surf, & dhdt_atm, dedq_atm, dtaudu_atm,dtaudv_atm, & w_atm, u_star, b_star, q_star, & - cd_m, cd_t, cd_q, & - ex_del_m, ex_del_h, ex_del_q, & !mp586 for 10m winds and 2m temp - temp_2m, u_10m, v_10m, & !mp586 for 10m winds and 2m temp - q_2m, rh_2m !Add 2m q and RH + cd_m, cd_t, cd_q, & + ex_del_m, ex_del_h, ex_del_q, & + temp_2m, u_10m, v_10m, & + q_2m, rh_2m real, dimension(1) :: q_surf - real, dimension(1) :: bucket_depth !RG Add bucket - real, dimension(1) :: depth_change_lh_1d !RG Add bucket - real, dimension(1) :: depth_change_conv_1d, depth_change_cond_1d !RG Add bucket - real :: max_bucket_depth_land !RG Add bucket + real, dimension(1) :: bucket_depth + real, dimension(1) :: depth_change_lh_1d + real, dimension(1) :: depth_change_conv_1d, depth_change_cond_1d + real :: max_bucket_depth_land avail = .true. @@ -787,8 +783,8 @@ subroutine surface_flux_0d ( & call surface_flux_1d ( & t_atm, q_atm, u_atm, v_atm, p_atm, z_atm, & p_surf, t_surf, t_ca, q_surf, & - bucket, bucket_depth, max_bucket_depth_land, & !RG Add bucket - depth_change_lh_1d, depth_change_conv_1d, depth_change_cond_1d, & !RG Add bucket + bucket, bucket_depth, max_bucket_depth_land, & + depth_change_lh_1d, depth_change_conv_1d, depth_change_cond_1d, & u_surf, v_surf, & rough_mom, rough_heat, rough_moist, rough_scale, gust, & flux_t, flux_q, flux_r, flux_u, flux_v, & @@ -796,9 +792,9 @@ subroutine surface_flux_0d ( & w_atm, u_star, b_star, q_star, & dhdt_surf, dedt_surf, dedq_surf, drdt_surf, & dhdt_atm, dedq_atm, dtaudu_atm, dtaudv_atm, & - ex_del_m, ex_del_h, ex_del_q, & !mp586 for 10m winds and 2m temp - temp_2m, u_10m, v_10m, & !mp586 for 10m winds and 2m temp - q_2m, rh_2m, & !Add 2m q and RH + ex_del_m, ex_del_h, ex_del_q, & + temp_2m, u_10m, v_10m, & + q_2m, rh_2m, & dt, land, seawater, avail ) flux_t_0 = flux_t(1) @@ -822,22 +818,22 @@ subroutine surface_flux_0d ( & cd_m_0 = cd_m(1) cd_t_0 = cd_t(1) cd_q_0 = cd_q(1) - ex_del_m_0 = ex_del_m(1) !mp586 for 10m winds and 2m temp - ex_del_h_0 = ex_del_h(1) !mp586 for 10m winds and 2m temp - ex_del_q_0 = ex_del_q(1) !mp586 for 10m winds and 2m temp - temp_2m_0 = temp_2m(1) !mp586 for 10m winds and 2m temp - u_10m_0 = u_10m(1) !mp586 for 10m winds and 2m temp - v_10m_0 = v_10m(1) !mp586 for 10m winds and 2m temp - q_2m_0 = q_2m(1) !Add 2m q - rh_2m_0 = rh_2m(1) !Add 2m RH + ex_del_m_0 = ex_del_m(1) + ex_del_h_0 = ex_del_h(1) + ex_del_q_0 = ex_del_q(1) + temp_2m_0 = temp_2m(1) + u_10m_0 = u_10m(1) + v_10m_0 = v_10m(1) + q_2m_0 = q_2m(1) + rh_2m_0 = rh_2m(1) end subroutine surface_flux_0d subroutine surface_flux_2d ( & t_atm, q_atm_in, u_atm, v_atm, p_atm, z_atm, & p_surf, t_surf, t_ca, q_surf, & - bucket, bucket_depth, max_bucket_depth_land, & !RG Add bucket - depth_change_lh, depth_change_conv, depth_change_cond, & !RG Add bucket + bucket, bucket_depth, max_bucket_depth_land, & + depth_change_lh, depth_change_conv, depth_change_cond, & u_surf, v_surf, & rough_mom, rough_heat, rough_moist, rough_scale, gust, & flux_t, flux_q, flux_r, flux_u, flux_v, & @@ -845,9 +841,9 @@ subroutine surface_flux_2d ( & w_atm, u_star, b_star, q_star, & dhdt_surf, dedt_surf, dedq_surf, drdt_surf, & dhdt_atm, dedq_atm, dtaudu_atm, dtaudv_atm, & - ex_del_m, ex_del_h, ex_del_q, & !mp586 for 10m winds and 2m temp - temp_2m, u_10m, v_10m, & !mp586 for 10m winds and 2m temp - q_2m, rh_2m, & !Add 2m q and RH + ex_del_m, ex_del_h, ex_del_q, & + temp_2m, u_10m, v_10m, & + q_2m, rh_2m, & dt, land, seawater, avail ) ! ---- arguments ----------------------------------------------------------- @@ -863,16 +859,16 @@ subroutine surface_flux_2d ( & dhdt_atm, dedq_atm, dtaudu_atm,dtaudv_atm, & w_atm, u_star, b_star, q_star, & cd_m, cd_t, cd_q, & - ex_del_m, ex_del_h, ex_del_q, & !mp586 for 10m winds and 2m temp - temp_2m, u_10m, v_10m, & !mp586 for 10m winds and 2m temp - q_2m, rh_2m !Add 2m q and RH + ex_del_m, ex_del_h, ex_del_q, & + temp_2m, u_10m, v_10m, & + q_2m, rh_2m real, intent(inout), dimension(:,:) :: q_surf - logical, intent(in) :: bucket !RG Add bucket - real, intent(inout), dimension(:,:) :: bucket_depth ! RG Add bucket - real, intent(inout), dimension(:,:) :: depth_change_lh ! RG Add bucket - real, intent(in), dimension(:,:) :: depth_change_conv, depth_change_cond ! RG Add bucket - real, intent(in) :: max_bucket_depth_land ! RG Add bucket + logical, intent(in) :: bucket + real, intent(inout), dimension(:,:) :: bucket_depth + real, intent(inout), dimension(:,:) :: depth_change_lh + real, intent(in), dimension(:,:) :: depth_change_conv, depth_change_cond + real, intent(in) :: max_bucket_depth_land real, intent(in) :: dt ! ---- local vars ----------------------------------------------------------- @@ -882,8 +878,8 @@ subroutine surface_flux_2d ( & call surface_flux_1d ( & t_atm(:,j), q_atm_in(:,j), u_atm(:,j), v_atm(:,j), p_atm(:,j), z_atm(:,j), & p_surf(:,j), t_surf(:,j), t_ca(:,j), q_surf(:,j), & - bucket, bucket_depth(:,j), max_bucket_depth_land, & !RG Add bucket - depth_change_lh(:,j), depth_change_conv(:,j), depth_change_cond(:,j), & !RG Add bucket + bucket, bucket_depth(:,j), max_bucket_depth_land, & + depth_change_lh(:,j), depth_change_conv(:,j), depth_change_cond(:,j), & u_surf(:,j), v_surf(:,j), & rough_mom(:,j), rough_heat(:,j), rough_moist(:,j), rough_scale(:,j), gust(:,j), & flux_t(:,j), flux_q(:,j), flux_r(:,j), flux_u(:,j), flux_v(:,j), & @@ -891,8 +887,8 @@ subroutine surface_flux_2d ( & w_atm(:,j), u_star(:,j), b_star(:,j), q_star(:,j), & dhdt_surf(:,j), dedt_surf(:,j), dedq_surf(:,j), drdt_surf(:,j), & dhdt_atm(:,j), dedq_atm(:,j), dtaudu_atm(:,j), dtaudv_atm(:,j), & - ex_del_m(:,j), ex_del_h(:,j), ex_del_q(:,j), & !mp586 for 10m winds and 2m temp - temp_2m(:,j), u_10m(:,j), v_10m(:,j), & !mp586 for 10m winds and 2m temp + ex_del_m(:,j), ex_del_h(:,j), ex_del_q(:,j), & + temp_2m(:,j), u_10m(:,j), v_10m(:,j), & q_2m(:,j), rh_2m(:,j), & dt, land(:,j), seawater(:,j), avail(:,j) ) end do @@ -910,6 +906,7 @@ subroutine surface_flux_init ! read namelist #ifdef INTERNAL_FILE_NML read (input_nml_file, surface_flux_nml, iostat=io) + ierr = check_nml_error(io,'surface_flux_nml') #else if ( file_exist('input.nml')) then unit = open_namelist_file () diff --git a/src/extra/env/deepthought2 b/src/extra/env/deepthought2 new file mode 100644 index 000000000..4500ad1b1 --- /dev/null +++ b/src/extra/env/deepthought2 @@ -0,0 +1,13 @@ +echo loadmodules for Deepthought2 + +module load intel/2015.0.3.032 +module load openmpi/1.8.6 +module load netcdf/4.3.3.1 +module load netcdf-fortran + +export NETCDF=$NETCDF_FORTRAN_ROOT +export GFDL_MKMF_TEMPLATE=dt2 +export LD_LIBRARY_PATH=$NETCDF_LIBDIR:$NETCDF_FORTRAN_LIBDIR:$LD_LIBRARY_PATH +export MPI_LIB= +export F90=mpifort +export CC=mpicc diff --git a/src/extra/env/fasrc b/src/extra/env/fasrc new file mode 100644 index 000000000..940299013 --- /dev/null +++ b/src/extra/env/fasrc @@ -0,0 +1,11 @@ +echo loadmodules for Isca on Harvard FASRC Cluster Cannon/Odyssey + +export F90=mpif90; echo $F90; +export CC=mpicc + +module purge +module load intel/17.0.4-fasrc01 +#module load hdf5/1.10.1-fasrc03 +module load intel/17.0.4-fasrc01 openmpi/2.1.0-fasrc02 netcdf-fortran/4.4.4-fasrc06 +module load zlib/1.2.8-fasrc07 +#module load netcdf/4.3.2-fasrc03 diff --git a/src/extra/env/gekko b/src/extra/env/gekko new file mode 100644 index 000000000..3ea79b577 --- /dev/null +++ b/src/extra/env/gekko @@ -0,0 +1,10 @@ +echo loadmodules for Isca on NTU Gekko + +export F90=mpiifort; echo $F90; +export CC=mpiicc + +module purge +module load intel/2018u3 +module load hdf/1.10.0 +module load netcdf-fortran/4.4.4 +module load netcdf/4.6.1 diff --git a/src/extra/env/maths2 b/src/extra/env/maths2 new file mode 100644 index 000000000..e693bc85e --- /dev/null +++ b/src/extra/env/maths2 @@ -0,0 +1,6 @@ +echo loadmodules for maths2 machines + +export F90=mpiifort +export CC=mpiicc + +export GFDL_MKMF_TEMPLATE=maths2 diff --git a/src/extra/env/nyu-prince b/src/extra/env/nyu-prince new file mode 100644 index 000000000..702c4edf9 --- /dev/null +++ b/src/extra/env/nyu-prince @@ -0,0 +1,10 @@ +#!/bin/bash + +echo load modules for Isca on NYU HPC prince system + +export F90=mpif90 +export CC=mpicc + +module purge +module load openmpi/intel/2.0.1 +module load netcdf/intel/4.7.2 diff --git a/src/extra/env/ubuntu_conda b/src/extra/env/ubuntu_conda new file mode 100644 index 000000000..ccbc1cabc --- /dev/null +++ b/src/extra/env/ubuntu_conda @@ -0,0 +1,7 @@ +echo "Loading basic ubuntu-conda environment" + +# this defaults to ia64, but we will use gfortran, not ifort +export GFDL_MKMF_TEMPLATE=ubuntu_conda + +export F90=mpifort +export CC=mpicc diff --git a/src/extra/model/barotropic/field_table b/src/extra/model/barotropic/field_table new file mode 100644 index 000000000..e69de29bb diff --git a/src/extra/model/barotropic/path_names b/src/extra/model/barotropic/path_names new file mode 100644 index 000000000..e7c619d86 --- /dev/null +++ b/src/extra/model/barotropic/path_names @@ -0,0 +1,138 @@ +atmos_solo/atmos_model.F90 +atmos_spectral_barotropic/atmosphere.F90 +atmos_spectral_barotropic/barotropic_diagnostics.F90 +atmos_spectral_barotropic/barotropic_dynamics.F90 +atmos_spectral_barotropic/barotropic_physics.F90 +atmos_spectral_barotropic/stirring.F90 +atmos_spectral/model/fv_advection.F90 +atmos_spectral/model/leapfrog.F90 +atmos_spectral/model/spectral_damping.F90 +atmos_spectral/tools/gauss_and_legendre.F90 +atmos_spectral/tools/grid_fourier.F90 +atmos_spectral/tools/spec_mpp.F90 +atmos_spectral/tools/spherical.F90 +atmos_spectral/tools/spherical_fourier.F90 +atmos_spectral/tools/transforms.F90 +shared/constants/constants.F90 +shared/diag_manager/diag_axis.F90 +shared/diag_manager/diag_data.F90 +shared/diag_manager/diag_grid.F90 +shared/diag_manager/diag_manager.F90 +shared/diag_manager/diag_output.F90 +shared/diag_manager/diag_table.F90 +shared/diag_manager/diag_util.F90 +shared/fft/fft99.F90 +shared/fft/fft.F90 +shared/field_manager/field_manager.F90 +shared/field_manager/fm_util.F90 +shared/field_manager/parse.inc +shared/fms/fms.F90 +shared/fms/fms_io.F90 +shared/fms/read_data_2d.inc +shared/fms/read_data_3d.inc +shared/fms/read_data_4d.inc +shared/fms/test_fms_io.F90 +shared/fms/write_data.inc +shared/include/fms_platform.h +shared/memutils/memuse.c +shared/memutils/memutils.F90 +shared/mosaic/constant.h +shared/mosaic/create_xgrid.c +shared/mosaic/create_xgrid.h +shared/mosaic/gradient_c2l.c +shared/mosaic/gradient_c2l.h +shared/mosaic/gradient.F90 +shared/mosaic/grid.F90 +shared/mosaic/interp.c +shared/mosaic/interp.h +shared/mosaic/mosaic.F90 +shared/mosaic/mosaic_util.c +shared/mosaic/mosaic_util.h +shared/mosaic/read_mosaic.c +shared/mosaic/read_mosaic.h +shared/mpp/affinity.c +shared/mpp/include/mpp_chksum.h +shared/mpp/include/mpp_chksum_int.h +shared/mpp/include/mpp_chksum_scalar.h +shared/mpp/include/mpp_comm.inc +shared/mpp/include/mpp_comm_mpi.inc +shared/mpp/include/mpp_comm_nocomm.inc +shared/mpp/include/mpp_comm_sma.inc +shared/mpp/include/mpp_data_mpi.inc +shared/mpp/include/mpp_data_nocomm.inc +shared/mpp/include/mpp_data_sma.inc +shared/mpp/include/mpp_define_nest_domains.inc +shared/mpp/include/mpp_do_check.h +shared/mpp/include/mpp_do_checkV.h +shared/mpp/include/mpp_do_get_boundary.h +shared/mpp/include/mpp_do_global_field.h +shared/mpp/include/mpp_domains_comm.inc +shared/mpp/include/mpp_domains_define.inc +shared/mpp/include/mpp_domains_misc.inc +shared/mpp/include/mpp_domains_reduce.inc +shared/mpp/include/mpp_domains_util.inc +shared/mpp/include/mpp_do_redistribute.h +shared/mpp/include/mpp_do_update_ad.h +shared/mpp/include/mpp_do_update.h +shared/mpp/include/mpp_do_update_nest.h +shared/mpp/include/mpp_do_update_nonblock.h +shared/mpp/include/mpp_do_updateV_ad.h +shared/mpp/include/mpp_do_updateV.h +shared/mpp/include/mpp_do_updateV_nonblock.h +shared/mpp/include/mpp_error_a_a.h +shared/mpp/include/mpp_error_a_s.h +shared/mpp/include/mpp_error_s_a.h +shared/mpp/include/mpp_error_s_s.h +shared/mpp/include/mpp_gather.h +shared/mpp/include/mpp_get_boundary.h +shared/mpp/include/mpp_global_field.h +shared/mpp/include/mpp_global_reduce.h +shared/mpp/include/mpp_global_sum_ad.h +shared/mpp/include/mpp_global_sum.h +shared/mpp/include/mpp_global_sum_tl.h +shared/mpp/include/mpp_io_connect.inc +shared/mpp/include/mpp_io_misc.inc +shared/mpp/include/mpp_io_read.inc +shared/mpp/include/mpp_io_util.inc +shared/mpp/include/mpp_io_write.inc +shared/mpp/include/mpp_read_2Ddecomp.h +shared/mpp/include/mpp_reduce_mpi.h +shared/mpp/include/mpp_reduce_nocomm.h +shared/mpp/include/mpp_reduce_sma.h +shared/mpp/include/mpp_sum.inc +shared/mpp/include/mpp_sum_mpi.h +shared/mpp/include/mpp_sum_nocomm.h +shared/mpp/include/mpp_sum_sma.h +shared/mpp/include/mpp_transmit.inc +shared/mpp/include/mpp_transmit_mpi.h +shared/mpp/include/mpp_transmit_nocomm.h +shared/mpp/include/mpp_transmit_sma.h +shared/mpp/include/mpp_update_domains2D_ad.h +shared/mpp/include/mpp_update_domains2D.h +shared/mpp/include/mpp_update_domains2D_nonblock.h +shared/mpp/include/mpp_update_nest_domains.h +shared/mpp/include/mpp_util.inc +shared/mpp/include/mpp_util_mpi.inc +shared/mpp/include/mpp_util_nocomm.inc +shared/mpp/include/mpp_util_sma.inc +shared/mpp/include/mpp_write_2Ddecomp.h +shared/mpp/include/mpp_write.h +shared/mpp/include/system_clock.h +shared/mpp/mpp_data.F90 +shared/mpp/mpp_domains.F90 +shared/mpp/mpp.F90 +shared/mpp/mpp_io.F90 +shared/mpp/mpp_memutils.F90 +shared/mpp/mpp_parameter.F90 +shared/mpp/mpp_pset.F90 +shared/mpp/mpp_utilities.F90 +shared/mpp/nsclock.c +shared/mpp/test_mpp_domains.F90 +shared/mpp/test_mpp.F90 +shared/mpp/test_mpp_io.F90 +shared/mpp/test_mpp_pset.F90 +shared/mpp/threadloc.c +shared/platform/platform.F90 +shared/time_manager/get_cal_time.F90 +shared/time_manager/time_manager.F90 +shared/tracer_manager/tracer_manager.F90 diff --git a/src/extra/model/column/field_table b/src/extra/model/column/field_table new file mode 100644 index 000000000..c2d63b9dd --- /dev/null +++ b/src/extra/model/column/field_table @@ -0,0 +1,10 @@ + +"TRACER", "atmos_mod", "sphum" + "longname", "specific humidity" + "units", "kg/kg" + "numerical_representation", "grid" + "hole_filling", "off" + "advect_vert", "finite_volume_parabolic" + "robert_filter", "on" + "profile_type", "fixed", "surface_value=0.0" / + diff --git a/src/extra/model/column/path_names b/src/extra/model/column/path_names new file mode 100644 index 000000000..e270ee283 --- /dev/null +++ b/src/extra/model/column/path_names @@ -0,0 +1,262 @@ +atmos_column/column.F90 +atmos_column/column_init_cond.F90 +atmos_column/column_grid.F90 +atmos_column/column_initialize_fields.F90 +atmos_param/diffusivity/diffusivity.F90 +atmos_param/edt/edt.F90 +atmos_param/entrain/entrain.F90 +atmos_param/hs_forcing/hs_forcing.F90 +atmos_param/lscale_cond/lscale_cond.F90 +atmos_param/my25_turb/my25_turb.F90 +atmos_param/qe_moist_convection/qe_moist_convection.F90 +atmos_param/betts_miller/betts_miller.f90 +atmos_param/ras/ras.f90 +atmos_param/sea_esf_rad/null/rad_utilities.F90 +atmos_param/shallow_conv/shallow_conv.F90 +atmos_param/stable_bl_turb/stable_bl_turb.F90 +atmos_param/strat_cloud/null/strat_cloud.F90 +atmos_param/cloud_simple/cloud_simple.F90 +atmos_param/cloud_simple/lcl.F90 +atmos_param/cloud_simple/large_scale_cloud.F90 +atmos_param/cloud_simple/marine_strat_cloud.F90 +atmos_param/cloud_simple/cloud_cover_diags.F90 +atmos_param/cloud_simple/cloud_spookie.F90 +atmos_param/two_stream_gray_rad/two_stream_gray_rad.F90 +atmos_param/qflux/qflux.f90 +atmos_param/monin_obukhov/monin_obukhov_interfaces.h +atmos_param/monin_obukhov/monin_obukhov_kernel.F90 +atmos_param/monin_obukhov/monin_obukhov.F90 +atmos_param/dry_convection/dry_convection.f90 +atmos_param/rayleigh_bottom_drag/rayleigh_bottom_drag.F90 +atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/modules/parkind.f90 +atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/modules/parrrtm.f90 +atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/modules/rrlw_cld.f90 +atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/modules/rrlw_con.f90 +atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/modules/rrlw_kg01.f90 +atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/modules/rrlw_kg02.f90 +atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/modules/rrlw_kg03.f90 +atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/modules/rrlw_kg04.f90 +atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/modules/rrlw_kg05.f90 +atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/modules/rrlw_kg06.f90 +atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/modules/rrlw_kg07.f90 +atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/modules/rrlw_kg08.f90 +atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/modules/rrlw_kg09.f90 +atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/modules/rrlw_kg10.f90 +atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/modules/rrlw_kg11.f90 +atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/modules/rrlw_kg12.f90 +atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/modules/rrlw_kg13.f90 +atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/modules/rrlw_kg14.f90 +atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/modules/rrlw_kg15.f90 +atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/modules/rrlw_kg16.f90 +atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/modules/rrlw_ncpar.f90 +atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/modules/rrlw_ref.f90 +atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/modules/rrlw_tbl.f90 +atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/modules/rrlw_vsn.f90 +atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/modules/rrlw_wvn.f90 +atmos_param/rrtm_radiation/rrtm_radiation.F90 +atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/src/mcica_random_numbers.f90 +atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/src/rrtmg_lw_cldprop.f90 +atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/src/rrtmg_lw_rad.nomcica.f90 +atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/src/rrtmg_lw_rtrn.f90 +atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/src/rrtmg_lw_setcoef.f90 +atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/src/mcica_subcol_gen_lw.f90 +atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/src/rrtmg_lw_init.f90 +atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/src/rrtmg_lw_rtrnmc.f90 +atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/src/rrtmg_lw_taumol.f90 +atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/src/rrtmg_lw_cldprmc.f90 +atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/src/rrtmg_lw_k_g.f90 +atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/src/rrtmg_lw_rtrnmr.f90 +atmos_param/rrtm_radiation/rrtmg_sw/gcm_model/modules/parkind.f90 +atmos_param/rrtm_radiation/rrtmg_sw/gcm_model/modules/parrrsw.f90 +atmos_param/rrtm_radiation/rrtmg_sw/gcm_model/modules/rrsw_aer.f90 +atmos_param/rrtm_radiation/rrtmg_sw/gcm_model/modules/rrsw_cld.f90 +atmos_param/rrtm_radiation/rrtmg_sw/gcm_model/modules/rrsw_con.f90 +atmos_param/rrtm_radiation/rrtmg_sw/gcm_model/modules/rrsw_kg16.f90 +atmos_param/rrtm_radiation/rrtmg_sw/gcm_model/modules/rrsw_kg17.f90 +atmos_param/rrtm_radiation/rrtmg_sw/gcm_model/modules/rrsw_kg18.f90 +atmos_param/rrtm_radiation/rrtmg_sw/gcm_model/modules/rrsw_kg19.f90 +atmos_param/rrtm_radiation/rrtmg_sw/gcm_model/modules/rrsw_kg20.f90 +atmos_param/rrtm_radiation/rrtmg_sw/gcm_model/modules/rrsw_kg21.f90 +atmos_param/rrtm_radiation/rrtmg_sw/gcm_model/modules/rrsw_kg22.f90 +atmos_param/rrtm_radiation/rrtmg_sw/gcm_model/modules/rrsw_kg23.f90 +atmos_param/rrtm_radiation/rrtmg_sw/gcm_model/modules/rrsw_kg24.f90 +atmos_param/rrtm_radiation/rrtmg_sw/gcm_model/modules/rrsw_kg25.f90 +atmos_param/rrtm_radiation/rrtmg_sw/gcm_model/modules/rrsw_kg26.f90 +atmos_param/rrtm_radiation/rrtmg_sw/gcm_model/modules/rrsw_kg27.f90 +atmos_param/rrtm_radiation/rrtmg_sw/gcm_model/modules/rrsw_kg28.f90 +atmos_param/rrtm_radiation/rrtmg_sw/gcm_model/modules/rrsw_kg29.f90 +atmos_param/rrtm_radiation/rrtmg_sw/gcm_model/modules/rrsw_ncpar.f90 +atmos_param/rrtm_radiation/rrtmg_sw/gcm_model/modules/rrsw_ref.f90 +atmos_param/rrtm_radiation/rrtmg_sw/gcm_model/modules/rrsw_tbl.f90 +atmos_param/rrtm_radiation/rrtmg_sw/gcm_model/modules/rrsw_vsn.f90 +atmos_param/rrtm_radiation/rrtmg_sw/gcm_model/modules/rrsw_wvn.f90 +atmos_param/rrtm_radiation/rrtmg_sw/gcm_model/src/mcica_random_numbers.f90 +atmos_param/rrtm_radiation/rrtmg_sw/gcm_model/src/mcica_subcol_gen_sw.f90 +atmos_param/rrtm_radiation/rrtmg_sw/gcm_model/src/rrtmg_sw_rad.nomcica.f90 +atmos_param/rrtm_radiation/rrtmg_sw/gcm_model/src/rrtmg_sw_cldprop.f90 +atmos_param/rrtm_radiation/rrtmg_sw/gcm_model/src/rrtmg_sw_cldprmc.f90 +atmos_param/rrtm_radiation/rrtmg_sw/gcm_model/src/rrtmg_sw_init.f90 +atmos_param/rrtm_radiation/rrtmg_sw/gcm_model/src/rrtmg_sw_k_g.f90 +atmos_param/rrtm_radiation/rrtmg_sw/gcm_model/src/rrtmg_sw_reftra.f90 +atmos_param/rrtm_radiation/rrtmg_sw/gcm_model/src/rrtmg_sw_setcoef.f90 +atmos_param/rrtm_radiation/rrtmg_sw/gcm_model/src/rrtmg_sw_spcvrt.f90 +atmos_param/rrtm_radiation/rrtmg_sw/gcm_model/src/rrtmg_sw_spcvmc.f90 +atmos_param/rrtm_radiation/rrtmg_sw/gcm_model/src/rrtmg_sw_taumol.f90 +atmos_param/rrtm_radiation/rrtmg_sw/gcm_model/src/rrtmg_sw_vrtqdr.f90 +atmos_param/damping_driver/damping_driver.f90 +atmos_param/mg_drag/mg_drag.f90 +atmos_param/cg_drag/cg_drag.f90 +atmos_param/topo_drag/topo_drag.f90 +atmos_param/vert_diff/vert_diff.F90 +atmos_param/vert_turb_driver/vert_turb_driver.F90 +atmos_shared/interpolator/interpolator.F90 +atmos_shared/vert_advection/vert_advection.F90 +atmos_solo/atmos_model.F90 +atmos_spectral/driver/solo/atmosphere.F90 +atmos_spectral/driver/solo/idealized_moist_phys.F90 +atmos_spectral/driver/solo/mixed_layer.F90 +atmos_spectral/init/vert_coordinate.F90 +atmos_spectral/model/press_and_geopot.F90 +atmos_spectral/model/tracer_type.F90 +atmos_spectral/tools/spec_mpp.F90 +coupler/surface_flux.F90 +shared/axis_utils/axis_utils.F90 +shared/constants/constants.F90 +shared/astronomy/astronomy.f90 +shared/diag_manager/diag_axis.F90 +shared/diag_manager/diag_data.F90 +shared/diag_manager/diag_grid.F90 +shared/diag_manager/diag_manager.F90 +shared/diag_manager/diag_output.F90 +shared/diag_manager/diag_table.F90 +shared/diag_manager/diag_util.F90 +shared/fft/fft99.F90 +shared/fft/fft.F90 +shared/field_manager/field_manager.F90 +shared/field_manager/fm_util.F90 +shared/field_manager/parse.inc +shared/fms/fms.F90 +shared/fms/fms_io.F90 +shared/fms/read_data_2d.inc +shared/fms/read_data_3d.inc +shared/fms/read_data_4d.inc +shared/fms/test_fms_io.F90 +shared/fms/write_data.inc +shared/horiz_interp/horiz_interp_bicubic.F90 +shared/horiz_interp/horiz_interp_bilinear.F90 +shared/horiz_interp/horiz_interp_conserve.F90 +shared/horiz_interp/horiz_interp.F90 +shared/horiz_interp/horiz_interp_spherical.F90 +shared/horiz_interp/horiz_interp_type.F90 +shared/include/fms_platform.h +shared/memutils/memuse.c +shared/memutils/memutils.F90 +shared/mosaic/constant.h +shared/mosaic/create_xgrid.c +shared/mosaic/create_xgrid.h +shared/mosaic/gradient_c2l.c +shared/mosaic/gradient_c2l.h +shared/mosaic/gradient.F90 +shared/mosaic/grid.F90 +shared/mosaic/interp.c +shared/mosaic/interp.h +shared/mosaic/mosaic.F90 +shared/mosaic/mosaic_util.c +shared/mosaic/mosaic_util.h +shared/mosaic/read_mosaic.c +shared/mosaic/read_mosaic.h +shared/mpp/affinity.c +shared/mpp/include/mpp_chksum.h +shared/mpp/include/mpp_chksum_int.h +shared/mpp/include/mpp_chksum_scalar.h +shared/mpp/include/mpp_comm.inc +shared/mpp/include/mpp_comm_mpi.inc +shared/mpp/include/mpp_comm_nocomm.inc +shared/mpp/include/mpp_comm_sma.inc +shared/mpp/include/mpp_data_mpi.inc +shared/mpp/include/mpp_data_nocomm.inc +shared/mpp/include/mpp_data_sma.inc +shared/mpp/include/mpp_define_nest_domains.inc +shared/mpp/include/mpp_do_check.h +shared/mpp/include/mpp_do_checkV.h +shared/mpp/include/mpp_do_get_boundary.h +shared/mpp/include/mpp_do_global_field.h +shared/mpp/include/mpp_domains_comm.inc +shared/mpp/include/mpp_domains_define.inc +shared/mpp/include/mpp_domains_misc.inc +shared/mpp/include/mpp_domains_reduce.inc +shared/mpp/include/mpp_domains_util.inc +shared/mpp/include/mpp_do_redistribute.h +shared/mpp/include/mpp_do_update_ad.h +shared/mpp/include/mpp_do_update.h +shared/mpp/include/mpp_do_update_nest.h +shared/mpp/include/mpp_do_update_nonblock.h +shared/mpp/include/mpp_do_updateV_ad.h +shared/mpp/include/mpp_do_updateV.h +shared/mpp/include/mpp_do_updateV_nonblock.h +shared/mpp/include/mpp_error_a_a.h +shared/mpp/include/mpp_error_a_s.h +shared/mpp/include/mpp_error_s_a.h +shared/mpp/include/mpp_error_s_s.h +shared/mpp/include/mpp_gather.h +shared/mpp/include/mpp_get_boundary.h +shared/mpp/include/mpp_global_field.h +shared/mpp/include/mpp_global_reduce.h +shared/mpp/include/mpp_global_sum_ad.h +shared/mpp/include/mpp_global_sum.h +shared/mpp/include/mpp_global_sum_tl.h +shared/mpp/include/mpp_io_connect.inc +shared/mpp/include/mpp_io_misc.inc +shared/mpp/include/mpp_io_read.inc +shared/mpp/include/mpp_io_util.inc +shared/mpp/include/mpp_io_write.inc +shared/mpp/include/mpp_read_2Ddecomp.h +shared/mpp/include/mpp_reduce_mpi.h +shared/mpp/include/mpp_reduce_nocomm.h +shared/mpp/include/mpp_reduce_sma.h +shared/mpp/include/mpp_sum.inc +shared/mpp/include/mpp_sum_mpi.h +shared/mpp/include/mpp_sum_nocomm.h +shared/mpp/include/mpp_sum_sma.h +shared/mpp/include/mpp_transmit.inc +shared/mpp/include/mpp_transmit_mpi.h +shared/mpp/include/mpp_transmit_nocomm.h +shared/mpp/include/mpp_transmit_sma.h +shared/mpp/include/mpp_update_domains2D_ad.h +shared/mpp/include/mpp_update_domains2D.h +shared/mpp/include/mpp_update_domains2D_nonblock.h +shared/mpp/include/mpp_update_nest_domains.h +shared/mpp/include/mpp_util.inc +shared/mpp/include/mpp_util_mpi.inc +shared/mpp/include/mpp_util_nocomm.inc +shared/mpp/include/mpp_util_sma.inc +shared/mpp/include/mpp_write_2Ddecomp.h +shared/mpp/include/mpp_write.h +shared/mpp/include/system_clock.h +shared/mpp/mpp_data.F90 +shared/mpp/mpp_domains.F90 +shared/mpp/mpp.F90 +shared/mpp/mpp_io.F90 +shared/mpp/mpp_memutils.F90 +shared/mpp/mpp_parameter.F90 +shared/mpp/mpp_pset.F90 +shared/mpp/mpp_utilities.F90 +shared/mpp/nsclock.c +shared/mpp/test_mpp_domains.F90 +shared/mpp/test_mpp.F90 +shared/mpp/test_mpp_io.F90 +shared/mpp/test_mpp_pset.F90 +shared/mpp/threadloc.c +shared/platform/platform.F90 +shared/random_numbers/MersenneTwister.F90 +shared/random_numbers/random_numbers.F90 +shared/sat_vapor_pres/sat_vapor_pres.F90 +shared/sat_vapor_pres/sat_vapor_pres_k.F90 +shared/time_interp/time_interp_external.F90 +shared/time_interp/time_interp.F90 +shared/time_manager/get_cal_time.F90 +shared/time_manager/time_manager.F90 +shared/topography/gaussian_topog.F90 +shared/topography/topography.F90 +shared/tracer_manager/tracer_manager.F90 +shared/tridiagonal/tridiagonal.F90 diff --git a/src/extra/model/dry/path_names b/src/extra/model/dry/path_names index 635b45dfc..6a974f397 100644 --- a/src/extra/model/dry/path_names +++ b/src/extra/model/dry/path_names @@ -8,6 +8,12 @@ atmos_param/qe_moist_convection/qe_moist_convection.F90 atmos_param/betts_miller/betts_miller.f90 atmos_param/sea_esf_rad/null/rad_utilities.F90 atmos_param/shallow_conv/shallow_conv.F90 +atmos_param/cloud_simple/cloud_simple.F90 +atmos_param/cloud_simple/lcl.F90 +atmos_param/cloud_simple/large_scale_cloud.F90 +atmos_param/cloud_simple/marine_strat_cloud.F90 +atmos_param/cloud_simple/cloud_cover_diags.F90 +atmos_param/cloud_simple/cloud_spookie.F90 atmos_param/stable_bl_turb/stable_bl_turb.F90 atmos_param/strat_cloud/null/strat_cloud.F90 atmos_param/two_stream_gray_rad/two_stream_gray_rad.F90 diff --git a/src/extra/model/grey/path_names b/src/extra/model/grey/path_names index 96bc7b6bf..1bdea444c 100644 --- a/src/extra/model/grey/path_names +++ b/src/extra/model/grey/path_names @@ -10,6 +10,12 @@ atmos_param/sea_esf_rad/null/rad_utilities.F90 atmos_param/shallow_conv/shallow_conv.F90 atmos_param/stable_bl_turb/stable_bl_turb.F90 atmos_param/strat_cloud/null/strat_cloud.F90 +atmos_param/cloud_simple/cloud_simple.F90 +atmos_param/cloud_simple/lcl.F90 +atmos_param/cloud_simple/large_scale_cloud.F90 +atmos_param/cloud_simple/marine_strat_cloud.F90 +atmos_param/cloud_simple/cloud_cover_diags.F90 +atmos_param/cloud_simple/cloud_spookie.F90 atmos_param/two_stream_gray_rad/two_stream_gray_rad.F90 atmos_param/qflux/qflux.f90 atmos_param/monin_obukhov/monin_obukhov_interfaces.h diff --git a/src/extra/model/isca/path_names b/src/extra/model/isca/path_names index ee73b2499..4b08c4edb 100644 --- a/src/extra/model/isca/path_names +++ b/src/extra/model/isca/path_names @@ -11,6 +11,12 @@ atmos_param/sea_esf_rad/null/rad_utilities.F90 atmos_param/shallow_conv/shallow_conv.F90 atmos_param/stable_bl_turb/stable_bl_turb.F90 atmos_param/strat_cloud/null/strat_cloud.F90 +atmos_param/cloud_simple/cloud_simple.F90 +atmos_param/cloud_simple/lcl.F90 +atmos_param/cloud_simple/large_scale_cloud.F90 +atmos_param/cloud_simple/marine_strat_cloud.F90 +atmos_param/cloud_simple/cloud_cover_diags.F90 +atmos_param/cloud_simple/cloud_spookie.F90 atmos_param/two_stream_gray_rad/two_stream_gray_rad.F90 atmos_param/qflux/qflux.f90 atmos_param/monin_obukhov/monin_obukhov_interfaces.h @@ -43,7 +49,7 @@ atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/modules/rrlw_ref.f90 atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/modules/rrlw_tbl.f90 atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/modules/rrlw_vsn.f90 atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/modules/rrlw_wvn.f90 -atmos_param/rrtm_radiation/rrtm_radiation.f90 +atmos_param/rrtm_radiation/rrtm_radiation.F90 atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/src/mcica_random_numbers.f90 atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/src/rrtmg_lw_cldprop.f90 atmos_param/rrtm_radiation/rrtmg_lw/gcm_model/src/rrtmg_lw_rad.nomcica.f90 diff --git a/src/extra/model/shallow/path_names b/src/extra/model/shallow/path_names new file mode 100644 index 000000000..02dbdac8a --- /dev/null +++ b/src/extra/model/shallow/path_names @@ -0,0 +1,148 @@ +atmos_solo/atmos_model.F90 +atmos_spectral/model/fv_advection.F90 +atmos_spectral/model/leapfrog.F90 +atmos_spectral/model/spectral_damping.F90 +atmos_spectral_shallow/atmosphere.F90 +atmos_spectral_shallow/shallow_diagnostics.F90 +atmos_spectral_shallow/shallow_dynamics.F90 +atmos_spectral_shallow/shallow_physics.F90 +atmos_spectral_barotropic/stirring.F90 +atmos_spectral/tools/gauss_and_legendre.F90 +atmos_spectral/tools/grid_fourier.F90 +atmos_spectral/tools/spec_mpp.F90 +atmos_spectral/tools/spherical.F90 +atmos_spectral/tools/spherical_fourier.F90 +atmos_spectral/tools/transforms.F90 +shared/constants/constants.F90 +shared/diag_manager/diag_axis.F90 +shared/diag_manager/diag_data.F90 +shared/diag_manager/diag_grid.F90 +shared/diag_manager/diag_manager.F90 +shared/diag_manager/diag_output.F90 +shared/diag_manager/diag_table.F90 +shared/diag_manager/diag_util.F90 +shared/fft/fft99.F90 +shared/fft/fft.F90 +shared/field_manager/field_manager.F90 +shared/field_manager/fm_util.F90 +shared/field_manager/parse.inc +shared/fms/fms.F90 +shared/fms/fms_io.F90 +shared/fms/read_data_2d.inc +shared/fms/read_data_3d.inc +shared/fms/read_data_4d.inc +shared/fms/test_fms_io.F90 +shared/fms/write_data.inc +shared/include/fms_platform.h +shared/memutils/memuse.c +shared/memutils/memutils.F90 +shared/mosaic/constant.h +shared/mosaic/create_xgrid.c +shared/mosaic/create_xgrid.h +shared/mosaic/gradient_c2l.c +shared/mosaic/gradient_c2l.h +shared/mosaic/gradient.F90 +shared/mosaic/grid.F90 +shared/mosaic/interp.c +shared/mosaic/interp.h +shared/mosaic/mosaic.F90 +shared/mosaic/mosaic_util.c +shared/mosaic/mosaic_util.h +shared/mosaic/read_mosaic.c +shared/mosaic/read_mosaic.h +shared/mpp/affinity.c +shared/mpp/include/mpp_chksum.h +shared/mpp/include/mpp_chksum_int.h +shared/mpp/include/mpp_chksum_scalar.h +shared/mpp/include/mpp_comm.inc +shared/mpp/include/mpp_comm_mpi.inc +shared/mpp/include/mpp_comm_nocomm.inc +shared/mpp/include/mpp_comm_sma.inc +shared/mpp/include/mpp_data_mpi.inc +shared/mpp/include/mpp_data_nocomm.inc +shared/mpp/include/mpp_data_sma.inc +shared/mpp/include/mpp_define_nest_domains.inc +shared/mpp/include/mpp_do_check.h +shared/mpp/include/mpp_do_checkV.h +shared/mpp/include/mpp_do_get_boundary.h +shared/mpp/include/mpp_do_global_field.h +shared/mpp/include/mpp_domains_comm.inc +shared/mpp/include/mpp_domains_define.inc +shared/mpp/include/mpp_domains_misc.inc +shared/mpp/include/mpp_domains_reduce.inc +shared/mpp/include/mpp_domains_util.inc +shared/mpp/include/mpp_do_redistribute.h +shared/mpp/include/mpp_do_update_ad.h +shared/mpp/include/mpp_do_update.h +shared/mpp/include/mpp_do_update_nest.h +shared/mpp/include/mpp_do_update_nonblock.h +shared/mpp/include/mpp_do_updateV_ad.h +shared/mpp/include/mpp_do_updateV.h +shared/mpp/include/mpp_do_updateV_nonblock.h +shared/mpp/include/mpp_error_a_a.h +shared/mpp/include/mpp_error_a_s.h +shared/mpp/include/mpp_error_s_a.h +shared/mpp/include/mpp_error_s_s.h +shared/mpp/include/mpp_gather.h +shared/mpp/include/mpp_get_boundary.h +shared/mpp/include/mpp_global_field.h +shared/mpp/include/mpp_global_reduce.h +shared/mpp/include/mpp_global_sum_ad.h +shared/mpp/include/mpp_global_sum.h +shared/mpp/include/mpp_global_sum_tl.h +shared/mpp/include/mpp_io_connect.inc +shared/mpp/include/mpp_io_misc.inc +shared/mpp/include/mpp_io_read.inc +shared/mpp/include/mpp_io_util.inc +shared/mpp/include/mpp_io_write.inc +shared/mpp/include/mpp_read_2Ddecomp.h +shared/mpp/include/mpp_reduce_mpi.h +shared/mpp/include/mpp_reduce_nocomm.h +shared/mpp/include/mpp_reduce_sma.h +shared/mpp/include/mpp_sum.inc +shared/mpp/include/mpp_sum_mpi.h +shared/mpp/include/mpp_sum_nocomm.h +shared/mpp/include/mpp_sum_sma.h +shared/mpp/include/mpp_transmit.inc +shared/mpp/include/mpp_transmit_mpi.h +shared/mpp/include/mpp_transmit_nocomm.h +shared/mpp/include/mpp_transmit_sma.h +shared/mpp/include/mpp_update_domains2D_ad.h +shared/mpp/include/mpp_update_domains2D.h +shared/mpp/include/mpp_update_domains2D_nonblock.h +shared/mpp/include/mpp_update_nest_domains.h +shared/mpp/include/mpp_util.inc +shared/mpp/include/mpp_util_mpi.inc +shared/mpp/include/mpp_util_nocomm.inc +shared/mpp/include/mpp_util_sma.inc +shared/mpp/include/mpp_write_2Ddecomp.h +shared/mpp/include/mpp_write.h +shared/mpp/include/system_clock.h +shared/mpp/mpp_data.F90 +shared/mpp/mpp_domains.F90 +shared/mpp/mpp.F90 +shared/mpp/mpp_io.F90 +shared/mpp/mpp_memutils.F90 +shared/mpp/mpp_parameter.F90 +shared/mpp/mpp_pset.F90 +shared/mpp/mpp_utilities.F90 +shared/mpp/nsclock.c +shared/mpp/test_mpp_domains.F90 +shared/mpp/test_mpp.F90 +shared/mpp/test_mpp_io.F90 +shared/mpp/test_mpp_pset.F90 +shared/mpp/threadloc.c +shared/platform/platform.F90 +shared/time_manager/get_cal_time.F90 +shared/time_manager/time_manager.F90 +shared/tracer_manager/tracer_manager.F90 +atmos_shared/interpolator/interpolator.F90 +shared/horiz_interp/horiz_interp_bicubic.F90 +shared/horiz_interp/horiz_interp_bilinear.F90 +shared/horiz_interp/horiz_interp_conserve.F90 +shared/horiz_interp/horiz_interp.F90 +shared/horiz_interp/horiz_interp_spherical.F90 +shared/horiz_interp/horiz_interp_type.F90 +shared/time_interp/time_interp_external.F90 +shared/time_interp/time_interp.F90 +shared/axis_utils/axis_utils.F90 diff --git a/src/extra/model/socrates/path_names b/src/extra/model/socrates/path_names index 6cb698ede..59dc4cc24 100644 --- a/src/extra/model/socrates/path_names +++ b/src/extra/model/socrates/path_names @@ -11,6 +11,12 @@ atmos_param/sea_esf_rad/null/rad_utilities.F90 atmos_param/shallow_conv/shallow_conv.F90 atmos_param/stable_bl_turb/stable_bl_turb.F90 atmos_param/strat_cloud/null/strat_cloud.F90 +atmos_param/cloud_simple/cloud_simple.F90 +atmos_param/cloud_simple/lcl.F90 +atmos_param/cloud_simple/large_scale_cloud.F90 +atmos_param/cloud_simple/marine_strat_cloud.F90 +atmos_param/cloud_simple/cloud_cover_diags.F90 +atmos_param/cloud_simple/cloud_spookie.F90 atmos_param/two_stream_gray_rad/two_stream_gray_rad.F90 atmos_param/qflux/qflux.f90 atmos_param/monin_obukhov/monin_obukhov_interfaces.h @@ -328,89 +334,6 @@ atmos_param/socrates/src/trunk/src/radiance_core/monochromatic_radiance.F90 atmos_param/socrates/src/trunk/src/radiance_core/aggregate_cloud.F90 atmos_param/socrates/src/trunk/src/radiance_core/radiance_calc.F90 atmos_param/socrates/src/trunk/src/radiance_core/ir_source.F90 -atmos_param/socrates/src/trunk/src/scatter/conjugate_gradient_cloud_90.f90 -atmos_param/socrates/src/trunk/src/scatter/method_weight_pcf.f90 -atmos_param/socrates/src/trunk/src/scatter/select_weight_scatter_90.f90 -atmos_param/socrates/src/trunk/src/scatter/measure_particle_pcf.f90 -atmos_param/socrates/src/trunk/src/scatter/prec_integral_tcf.f90 -atmos_param/socrates/src/trunk/src/scatter/def_s_scat_prop.f90 -atmos_param/socrates/src/trunk/src/scatter/db_scatter_integral.f90 -atmos_param/socrates/src/trunk/src/scatter/weightings_90.f90 -atmos_param/socrates/src/trunk/src/scatter/def_db_ss_mono.f90 -atmos_param/socrates/src/trunk/src/scatter/cloud_fitting.f90 -atmos_param/socrates/src/trunk/src/scatter/shape_particle_pcf.f90 -atmos_param/socrates/src/trunk/src/scatter/number_particle_90.f90 -atmos_param/socrates/src/trunk/src/scatter/db_interp_ss_mono.f90 -atmos_param/socrates/src/trunk/src/scatter/db_read_single_wavelength.f90 -atmos_param/socrates/src/trunk/src/scatter/get_refract_index.f90 -atmos_param/socrates/src/trunk/src/scatter/read_scatter_block_90.f90 -atmos_param/socrates/src/trunk/src/scatter/proj_area_particle.f90 -atmos_param/socrates/src/trunk/src/scatter/line_search_cloud_90.f90 -atmos_param/socrates/src/trunk/src/scatter/volume_particle.f90 -atmos_param/socrates/src/trunk/src/scatter/distribution_pcf.f90 -atmos_param/socrates/src/trunk/src/scatter/size_integral_90.f90 -atmos_param/socrates/src/trunk/src/scatter/particle_size_90.f90 -atmos_param/socrates/src/trunk/src/scatter/max_size_acf.f90 -atmos_param/socrates/src/trunk/src/scatter/write_average_90.f90 -atmos_param/socrates/src/trunk/src/scatter/adt_mitchell96.f90 -atmos_param/socrates/src/trunk/src/scatter/get_db_wavelengths.f90 -atmos_param/socrates/src/trunk/src/scatter/decompose_phf_90.f90 -atmos_param/socrates/src/trunk/src/scatter/cloud_fit_parm_acf.f90 -atmos_param/socrates/src/trunk/src/scatter/cloud_fit_90.f90 -atmos_param/socrates/src/trunk/src/scatter/ice_db_read_geometry.f90 -atmos_param/socrates/src/trunk/src/scatter/def_sct_db.f90 -atmos_param/socrates/src/trunk/src/scatter/open_average_90.f90 -atmos_param/socrates/src/trunk/src/scatter/def_size_dist.f90 -atmos_param/socrates/src/trunk/src/scatter/parm_integ_acf.f90 -atmos_param/socrates/src/trunk/src/scatter/bna_factor_ccf.f90 -atmos_param/socrates/src/trunk/src/scatter/def_db_crystal_geometry.f90 -atmos_param/socrates/src/trunk/src/scatter/get_wavelengths.f90 -atmos_param/socrates/src/trunk/src/scatter/weightings_single_90.f90 -atmos_param/socrates/src/trunk/src/scatter/scatter_algorithm_pcf.f90 -atmos_param/socrates/src/trunk/src/scatter/db_type_ucf.f90 -atmos_param/socrates/src/trunk/src/scatter/scatter_integral_90.f90 -atmos_param/socrates/src/trunk/src/correlated_k/voigt_profile.f90 -atmos_param/socrates/src/trunk/src/correlated_k/adjust_path.f90 -atmos_param/socrates/src/trunk/src/correlated_k/func_scale_90.f90 -atmos_param/socrates/src/trunk/src/correlated_k/read_pt_line_90.f90 -atmos_param/socrates/src/trunk/src/correlated_k/set_condition_ck_90.f90 -atmos_param/socrates/src/trunk/src/correlated_k/caviar_continuum_v1_0.f90 -atmos_param/socrates/src/trunk/src/correlated_k/rad_weight_90.f90 -atmos_param/socrates/src/trunk/src/correlated_k/select_weight_ck_90.f90 -atmos_param/socrates/src/trunk/src/correlated_k/trans_k_dist.f90 -atmos_param/socrates/src/trunk/src/correlated_k/type_residual_pcf.f90 -atmos_param/socrates/src/trunk/src/correlated_k/map_shell.f90 -atmos_param/socrates/src/trunk/src/correlated_k/terminate_scale_90.f90 -atmos_param/socrates/src/trunk/src/correlated_k/ckd_continuum_v2_4.f90 -atmos_param/socrates/src/trunk/src/correlated_k/write_fit_90.f90 -atmos_param/socrates/src/trunk/src/correlated_k/ck_parm_acf.f90 -atmos_param/socrates/src/trunk/src/correlated_k/fit_parabola_90.f90 -atmos_param/socrates/src/trunk/src/correlated_k/func_scale_derivative_90.f90 -atmos_param/socrates/src/trunk/src/correlated_k/optimal_k.f90 -atmos_param/socrates/src/trunk/src/correlated_k/open_file_out_90.f90 -atmos_param/socrates/src/trunk/src/correlated_k/residual_gradient_90.f90 -atmos_param/socrates/src/trunk/src/correlated_k/scale_ck_fit_90.f90 -atmos_param/socrates/src/trunk/src/correlated_k/read_ref_pt_90.f90 -atmos_param/socrates/src/trunk/src/correlated_k/corr_k_single.f90 -atmos_param/socrates/src/trunk/src/correlated_k/planck_90.f90 -atmos_param/socrates/src/trunk/src/correlated_k/exponent_fit_90.f90 -atmos_param/socrates/src/trunk/src/correlated_k/conjugate_gradient_90.f90 -atmos_param/socrates/src/trunk/src/correlated_k/line_prof_corr_mod.f90 -atmos_param/socrates/src/trunk/src/correlated_k/set_extern_ckd_frn_data.f90 -atmos_param/socrates/src/trunk/src/correlated_k/set_g_point_90.f90 -atmos_param/socrates/src/trunk/src/correlated_k/bi_interp.f90 -atmos_param/socrates/src/trunk/src/correlated_k/residual_trans_90.f90 -atmos_param/socrates/src/trunk/src/correlated_k/set_extern_ckd_self_data.f90 -atmos_param/socrates/src/trunk/src/correlated_k/line_search_90.f90 -atmos_param/socrates/src/trunk/src/correlated_k/read_hitran.f90 -atmos_param/socrates/src/trunk/src/correlated_k/read_nc.f90 -atmos_param/socrates/src/trunk/src/correlated_k/ckd_extern_data.f90 -atmos_param/socrates/src/trunk/src/correlated_k/def_hitran_record.f90 -atmos_param/socrates/src/trunk/src/correlated_k/d_planck_90.f90 -atmos_param/socrates/src/trunk/src/correlated_k/scale_parameters_acf.f90 -atmos_param/socrates/src/trunk/src/correlated_k/offset_residual_trans_acf.f90 -atmos_param/socrates/src/trunk/src/correlated_k/hitran_cnst.f90 -atmos_param/socrates/src/trunk/src/correlated_k/ck_fit_pcf.f90 atmos_param/socrates/src/trunk/src/aux/qsat_wat.F90 atmos_param/socrates/src/trunk/src/aux/qsat_gill.F90 atmos_param/socrates/src/trunk/src/aux/write_cdf.f90 @@ -433,7 +356,6 @@ atmos_param/socrates/src/trunk/src/general/make_block_18.f90 atmos_param/socrates/src/trunk/src/general/remove_negative_gas_90.f90 atmos_param/socrates/src/trunk/src/general/make_block_17.f90 atmos_param/socrates/src/trunk/src/general/make_block_1.f90 -atmos_param/socrates/src/trunk/src/general/solar_intensity_90.f90 atmos_param/socrates/src/trunk/src/general/make_block_19.f90 atmos_param/socrates/src/trunk/src/general/make_block_0.f90 atmos_param/socrates/src/trunk/src/general/make_block_10.f90 @@ -483,8 +405,6 @@ atmos_param/socrates/src/trunk/src/radiation_control/set_moist_aerosol_propertie atmos_param/socrates/src/trunk/src/radiation_control/mcica_mod.F90 atmos_param/socrates/src/trunk/src/radiation_control/cld_generator_mod.F90 atmos_param/socrates/src/trunk/src/radiation_control/open_cloud_gen.F90 -atmos_param/socrates/src/trunk/src/um/out_nml.f90 -atmos_param/socrates/src/trunk/src/um/def_um_nml.f90 atmos_param/socrates/src/trunk/src/modules_core/errormessagelength_mod.F90 atmos_param/socrates/src/trunk/src/modules_core/dimensions_spec_ucf.F90 atmos_param/socrates/src/trunk/src/modules_core/rad_ccf.F90 @@ -496,11 +416,6 @@ atmos_param/socrates/src/trunk/src/modules_core/vectlib_mod.F90 atmos_param/socrates/src/trunk/src/modules_core/ereport_mod.F90 atmos_param/socrates/src/trunk/src/modules_core/realtype_rd.f90 atmos_param/socrates/src/trunk/src/modules_core/filenamelength_mod.F90 -atmos_param/socrates/src/trunk/src/scatter/grow_particles.f -atmos_param/socrates/src/trunk/src/scatter/mie_scatter.f -atmos_param/socrates/src/trunk/src/scatter/adt_integral.f -atmos_param/socrates/src/trunk/src/scatter/gamma_fnc.f -atmos_param/socrates/src/trunk/src/scatter/refractive_index.f atmos_param/socrates/src/trunk/src/aux/output_vert_cdl.f atmos_param/socrates/src/trunk/src/aux/assign_input_opt_cdf.f atmos_param/socrates/src/trunk/src/aux/assign_input_ss_cdl.f @@ -583,6 +498,7 @@ atmos_param/socrates/src/trunk/src/general/read_word.f atmos_param/socrates/interface/read_control.F90 atmos_param/socrates/interface/set_atm.F90 atmos_param/socrates/interface/set_cld.F90 +atmos_param/socrates/interface/socrates_set_cld.F90 atmos_param/socrates/interface/set_dimen.F90 atmos_param/socrates/interface/socrates_config_mod.f90 atmos_param/socrates/interface/compress_spectrum.F90 diff --git a/src/extra/model/socrates_column/field_table b/src/extra/model/socrates_column/field_table new file mode 100644 index 000000000..c2d63b9dd --- /dev/null +++ b/src/extra/model/socrates_column/field_table @@ -0,0 +1,10 @@ + +"TRACER", "atmos_mod", "sphum" + "longname", "specific humidity" + "units", "kg/kg" + "numerical_representation", "grid" + "hole_filling", "off" + "advect_vert", "finite_volume_parabolic" + "robert_filter", "on" + "profile_type", "fixed", "surface_value=0.0" / + diff --git a/src/extra/model/socrates_column/path_names b/src/extra/model/socrates_column/path_names new file mode 100644 index 000000000..92c7dbe95 --- /dev/null +++ b/src/extra/model/socrates_column/path_names @@ -0,0 +1,584 @@ +atmos_column/column.F90 +atmos_column/column_init_cond.F90 +atmos_column/column_grid.F90 +atmos_column/column_initialize_fields.F90 +atmos_param/diffusivity/diffusivity.F90 +atmos_param/edt/edt.F90 +atmos_param/entrain/entrain.F90 +atmos_param/hs_forcing/hs_forcing.F90 +atmos_param/lscale_cond/lscale_cond.F90 +atmos_param/my25_turb/my25_turb.F90 +atmos_param/qe_moist_convection/qe_moist_convection.F90 +atmos_param/betts_miller/betts_miller.f90 +atmos_param/ras/ras.f90 +atmos_param/sea_esf_rad/null/rad_utilities.F90 +atmos_param/shallow_conv/shallow_conv.F90 +atmos_param/stable_bl_turb/stable_bl_turb.F90 +atmos_param/strat_cloud/null/strat_cloud.F90 +atmos_param/cloud_simple/cloud_simple.F90 +atmos_param/cloud_simple/lcl.F90 +atmos_param/cloud_simple/large_scale_cloud.F90 +atmos_param/cloud_simple/marine_strat_cloud.F90 +atmos_param/cloud_simple/cloud_cover_diags.F90 +atmos_param/cloud_simple/cloud_spookie.F90 +atmos_param/two_stream_gray_rad/two_stream_gray_rad.F90 +atmos_param/qflux/qflux.f90 +atmos_param/monin_obukhov/monin_obukhov_interfaces.h +atmos_param/monin_obukhov/monin_obukhov_kernel.F90 +atmos_param/monin_obukhov/monin_obukhov.F90 +atmos_param/dry_convection/dry_convection.f90 +atmos_param/rayleigh_bottom_drag/rayleigh_bottom_drag.F90 +atmos_param/damping_driver/damping_driver.f90 +atmos_param/mg_drag/mg_drag.f90 +atmos_param/cg_drag/cg_drag.f90 +atmos_param/topo_drag/topo_drag.f90 +atmos_param/vert_diff/vert_diff.F90 +atmos_param/vert_turb_driver/vert_turb_driver.F90 +atmos_shared/interpolator/interpolator.F90 +atmos_shared/vert_advection/vert_advection.F90 +atmos_solo/atmos_model.F90 +atmos_spectral/driver/solo/atmosphere.F90 +atmos_spectral/driver/solo/idealized_moist_phys.F90 +atmos_spectral/driver/solo/mixed_layer.F90 +atmos_spectral/init/vert_coordinate.F90 +atmos_spectral/model/press_and_geopot.F90 +atmos_spectral/model/tracer_type.F90 +atmos_spectral/tools/spec_mpp.F90 +coupler/surface_flux.F90 +shared/axis_utils/axis_utils.F90 +shared/constants/constants.F90 +shared/astronomy/astronomy.f90 +shared/diag_manager/diag_axis.F90 +shared/diag_manager/diag_data.F90 +shared/diag_manager/diag_grid.F90 +shared/diag_manager/diag_manager.F90 +shared/diag_manager/diag_output.F90 +shared/diag_manager/diag_table.F90 +shared/diag_manager/diag_util.F90 +shared/fft/fft99.F90 +shared/fft/fft.F90 +shared/field_manager/field_manager.F90 +shared/field_manager/fm_util.F90 +shared/field_manager/parse.inc +shared/fms/fms.F90 +shared/fms/fms_io.F90 +shared/fms/read_data_2d.inc +shared/fms/read_data_3d.inc +shared/fms/read_data_4d.inc +shared/fms/test_fms_io.F90 +shared/fms/write_data.inc +shared/horiz_interp/horiz_interp_bicubic.F90 +shared/horiz_interp/horiz_interp_bilinear.F90 +shared/horiz_interp/horiz_interp_conserve.F90 +shared/horiz_interp/horiz_interp.F90 +shared/horiz_interp/horiz_interp_spherical.F90 +shared/horiz_interp/horiz_interp_type.F90 +shared/include/fms_platform.h +shared/memutils/memuse.c +shared/memutils/memutils.F90 +shared/mosaic/constant.h +shared/mosaic/create_xgrid.c +shared/mosaic/create_xgrid.h +shared/mosaic/gradient_c2l.c +shared/mosaic/gradient_c2l.h +shared/mosaic/gradient.F90 +shared/mosaic/grid.F90 +shared/mosaic/interp.c +shared/mosaic/interp.h +shared/mosaic/mosaic.F90 +shared/mosaic/mosaic_util.c +shared/mosaic/mosaic_util.h +shared/mosaic/read_mosaic.c +shared/mosaic/read_mosaic.h +shared/mpp/affinity.c +shared/mpp/include/mpp_chksum.h +shared/mpp/include/mpp_chksum_int.h +shared/mpp/include/mpp_chksum_scalar.h +shared/mpp/include/mpp_comm.inc +shared/mpp/include/mpp_comm_mpi.inc +shared/mpp/include/mpp_comm_nocomm.inc +shared/mpp/include/mpp_comm_sma.inc +shared/mpp/include/mpp_data_mpi.inc +shared/mpp/include/mpp_data_nocomm.inc +shared/mpp/include/mpp_data_sma.inc +shared/mpp/include/mpp_define_nest_domains.inc +shared/mpp/include/mpp_do_check.h +shared/mpp/include/mpp_do_checkV.h +shared/mpp/include/mpp_do_get_boundary.h +shared/mpp/include/mpp_do_global_field.h +shared/mpp/include/mpp_domains_comm.inc +shared/mpp/include/mpp_domains_define.inc +shared/mpp/include/mpp_domains_misc.inc +shared/mpp/include/mpp_domains_reduce.inc +shared/mpp/include/mpp_domains_util.inc +shared/mpp/include/mpp_do_redistribute.h +shared/mpp/include/mpp_do_update_ad.h +shared/mpp/include/mpp_do_update.h +shared/mpp/include/mpp_do_update_nest.h +shared/mpp/include/mpp_do_update_nonblock.h +shared/mpp/include/mpp_do_updateV_ad.h +shared/mpp/include/mpp_do_updateV.h +shared/mpp/include/mpp_do_updateV_nonblock.h +shared/mpp/include/mpp_error_a_a.h +shared/mpp/include/mpp_error_a_s.h +shared/mpp/include/mpp_error_s_a.h +shared/mpp/include/mpp_error_s_s.h +shared/mpp/include/mpp_gather.h +shared/mpp/include/mpp_get_boundary.h +shared/mpp/include/mpp_global_field.h +shared/mpp/include/mpp_global_reduce.h +shared/mpp/include/mpp_global_sum_ad.h +shared/mpp/include/mpp_global_sum.h +shared/mpp/include/mpp_global_sum_tl.h +shared/mpp/include/mpp_io_connect.inc +shared/mpp/include/mpp_io_misc.inc +shared/mpp/include/mpp_io_read.inc +shared/mpp/include/mpp_io_util.inc +shared/mpp/include/mpp_io_write.inc +shared/mpp/include/mpp_read_2Ddecomp.h +shared/mpp/include/mpp_reduce_mpi.h +shared/mpp/include/mpp_reduce_nocomm.h +shared/mpp/include/mpp_reduce_sma.h +shared/mpp/include/mpp_sum.inc +shared/mpp/include/mpp_sum_mpi.h +shared/mpp/include/mpp_sum_nocomm.h +shared/mpp/include/mpp_sum_sma.h +shared/mpp/include/mpp_transmit.inc +shared/mpp/include/mpp_transmit_mpi.h +shared/mpp/include/mpp_transmit_nocomm.h +shared/mpp/include/mpp_transmit_sma.h +shared/mpp/include/mpp_update_domains2D_ad.h +shared/mpp/include/mpp_update_domains2D.h +shared/mpp/include/mpp_update_domains2D_nonblock.h +shared/mpp/include/mpp_update_nest_domains.h +shared/mpp/include/mpp_util.inc +shared/mpp/include/mpp_util_mpi.inc +shared/mpp/include/mpp_util_nocomm.inc +shared/mpp/include/mpp_util_sma.inc +shared/mpp/include/mpp_write_2Ddecomp.h +shared/mpp/include/mpp_write.h +shared/mpp/include/system_clock.h +shared/mpp/mpp_data.F90 +shared/mpp/mpp_domains.F90 +shared/mpp/mpp.F90 +shared/mpp/mpp_io.F90 +shared/mpp/mpp_memutils.F90 +shared/mpp/mpp_parameter.F90 +shared/mpp/mpp_pset.F90 +shared/mpp/mpp_utilities.F90 +shared/mpp/nsclock.c +shared/mpp/test_mpp_domains.F90 +shared/mpp/test_mpp.F90 +shared/mpp/test_mpp_io.F90 +shared/mpp/test_mpp_pset.F90 +shared/mpp/threadloc.c +shared/platform/platform.F90 +shared/random_numbers/MersenneTwister.F90 +shared/random_numbers/random_numbers.F90 +shared/sat_vapor_pres/sat_vapor_pres.F90 +shared/sat_vapor_pres/sat_vapor_pres_k.F90 +shared/time_interp/time_interp_external.F90 +shared/time_interp/time_interp.F90 +shared/time_manager/get_cal_time.F90 +shared/time_manager/time_manager.F90 +shared/topography/gaussian_topog.F90 +shared/topography/topography.F90 +shared/tracer_manager/tracer_manager.F90 +shared/tridiagonal/tridiagonal.F90 +atmos_param/socrates/src/trunk/src/aux/cdf_struc.finc +atmos_param/socrates/src/trunk/src/aux/cdl_struc.finc +atmos_param/socrates/src/trunk/src/aux/dec_disort.finc +atmos_param/socrates/src/trunk/src/aux/call_disort.finc +atmos_param/socrates/src/trunk/src/general/batch_error_main.finc +atmos_param/socrates/src/trunk/src/general/aerosol_component.finc +atmos_param/socrates/src/trunk/src/radiance_core/gauss_angle.F90 +atmos_param/socrates/src/trunk/src/radiance_core/solver_mix_direct.F90 +atmos_param/socrates/src/trunk/src/radiance_core/set_dirn_weights.F90 +atmos_param/socrates/src/trunk/src/radiance_core/calc_brdf.F90 +atmos_param/socrates/src/trunk/src/radiance_core/triple_solar_source.F90 +atmos_param/socrates/src/trunk/src/radiance_core/eval_uplm.F90 +atmos_param/socrates/src/trunk/src/radiance_core/shell_sort.F90 +atmos_param/socrates/src/trunk/src/radiance_core/rad_pcf.F90 +atmos_param/socrates/src/trunk/src/radiance_core/check_phf_term.F90 +atmos_param/socrates/src/trunk/src/radiance_core/set_n_source_coeff.F90 +atmos_param/socrates/src/trunk/src/radiance_core/calc_surf_rad.F90 +atmos_param/socrates/src/trunk/src/radiance_core/solve_band_random_overlap.F90 +atmos_param/socrates/src/trunk/src/radiance_core/scale_absorb.F90 +atmos_param/socrates/src/trunk/src/radiance_core/calculate_density.F90 +atmos_param/socrates/src/trunk/src/radiance_core/calc_flux_ipa.F90 +atmos_param/socrates/src/trunk/src/radiance_core/opt_prop_ice_cloud.F90 +atmos_param/socrates/src/trunk/src/radiance_core/diff_planck_source_tbl.F90 +atmos_param/socrates/src/trunk/src/radiance_core/eig_sys.F90 +atmos_param/socrates/src/trunk/src/radiance_core/adjust_ir_radiance.F90 +atmos_param/socrates/src/trunk/src/radiance_core/set_rad_layer.F90 +atmos_param/socrates/src/trunk/src/radiance_core/def_dimen.F90 +atmos_param/socrates/src/trunk/src/radiance_core/mcica_column.F90 +atmos_param/socrates/src/trunk/src/radiance_core/interp1d.F90 +atmos_param/socrates/src/trunk/src/radiance_core/calc_uplm_zero.F90 +atmos_param/socrates/src/trunk/src/radiance_core/spline_fit.F90 +atmos_param/socrates/src/trunk/src/radiance_core/gaussian_weight_pcf.F90 +atmos_param/socrates/src/trunk/src/radiance_core/gas_list_pcf.F90 +atmos_param/socrates/src/trunk/src/radiance_core/trans_source_coeff.F90 +atmos_param/socrates/src/trunk/src/radiance_core/band_solver.F90 +atmos_param/socrates/src/trunk/src/radiance_core/mcica_sample.F90 +atmos_param/socrates/src/trunk/src/radiance_core/solver_mix_direct_hogan.F90 +atmos_param/socrates/src/trunk/src/radiance_core/calc_uplm_sol.F90 +atmos_param/socrates/src/trunk/src/radiance_core/sph_matrix_solver.F90 +atmos_param/socrates/src/trunk/src/radiance_core/def_spectrum.F90 +atmos_param/socrates/src/trunk/src/radiance_core/solver_triple_app_scat.F90 +atmos_param/socrates/src/trunk/src/radiance_core/grey_opt_prop.F90 +atmos_param/socrates/src/trunk/src/radiance_core/calc_gauss_weight_90.F90 +atmos_param/socrates/src/trunk/src/radiance_core/sum_k.F90 +atmos_param/socrates/src/trunk/src/radiance_core/ses_rescale_contm.F90 +atmos_param/socrates/src/trunk/src/radiance_core/spline_evaluate.F90 +atmos_param/socrates/src/trunk/src/radiance_core/cg_kappa_ms.F90 +atmos_param/socrates/src/trunk/src/radiance_core/def_bound.F90 +atmos_param/socrates/src/trunk/src/radiance_core/solve_band_random_overlap_resort_rebin.F90 +atmos_param/socrates/src/trunk/src/radiance_core/monochromatic_gas_flux.F90 +atmos_param/socrates/src/trunk/src/radiance_core/set_cloud_pointer.F90 +atmos_param/socrates/src/trunk/src/radiance_core/set_cloud_geometry.F90 +atmos_param/socrates/src/trunk/src/radiance_core/rescale_phase_fnc.F90 +atmos_param/socrates/src/trunk/src/radiance_core/monochromatic_radiance_sph.F90 +atmos_param/socrates/src/trunk/src/radiance_core/inter_k.F90 +atmos_param/socrates/src/trunk/src/radiance_core/solve_band_one_gas.F90 +atmos_param/socrates/src/trunk/src/radiance_core/calc_top_rad.F90 +atmos_param/socrates/src/trunk/src/radiance_core/set_n_cloud_parameter.F90 +atmos_param/socrates/src/trunk/src/radiance_core/set_level_weights.F90 +atmos_param/socrates/src/trunk/src/radiance_core/solve_band_k_eqv_scl.F90 +atmos_param/socrates/src/trunk/src/radiance_core/set_matrix_pentadiagonal.F90 +atmos_param/socrates/src/trunk/src/radiance_core/mix_app_scat.F90 +atmos_param/socrates/src/trunk/src/radiance_core/def_out.F90 +atmos_param/socrates/src/trunk/src/radiance_core/sol_scat_cos.F90 +atmos_param/socrates/src/trunk/src/radiance_core/copy_clr_full.F90 +atmos_param/socrates/src/trunk/src/radiance_core/eigenvalue_tri.F90 +atmos_param/socrates/src/trunk/src/radiance_core/solver_homogen_direct.F90 +atmos_param/socrates/src/trunk/src/radiance_core/rescale_continuum.F90 +atmos_param/socrates/src/trunk/src/radiance_core/mixed_solar_source.F90 +atmos_param/socrates/src/trunk/src/radiance_core/def_aer.F90 +atmos_param/socrates/src/trunk/src/radiance_core/opt_prop_inhom_corr_cairns.F90 +atmos_param/socrates/src/trunk/src/radiance_core/rebin_esft_terms.F90 +atmos_param/socrates/src/trunk/src/radiance_core/mix_column.F90 +atmos_param/socrates/src/trunk/src/radiance_core/prsc_gather_spline.F90 +atmos_param/socrates/src/trunk/src/radiance_core/calc_cg_coeff.F90 +atmos_param/socrates/src/trunk/src/radiance_core/inter_pt_lookup.F90 +atmos_param/socrates/src/trunk/src/radiance_core/opt_prop_ukca_aerosol.F90 +atmos_param/socrates/src/trunk/src/radiance_core/scale_wenyi.F90 +atmos_param/socrates/src/trunk/src/radiance_core/triple_column.F90 +atmos_param/socrates/src/trunk/src/radiance_core/single_scattering_all.F90 +atmos_param/socrates/src/trunk/src/radiance_core/inter_pt.F90 +atmos_param/socrates/src/trunk/src/radiance_core/overlap_coupled.F90 +atmos_param/socrates/src/trunk/src/radiance_core/def_cld.F90 +atmos_param/socrates/src/trunk/src/radiance_core/solar_coefficient_basic.F90 +atmos_param/socrates/src/trunk/src/radiance_core/build_sph_matrix.F90 +atmos_param/socrates/src/trunk/src/radiance_core/solve_band_k_eqv.F90 +atmos_param/socrates/src/trunk/src/radiance_core/augment_tiled_radiance.F90 +atmos_param/socrates/src/trunk/src/radiance_core/column_solver.F90 +atmos_param/socrates/src/trunk/src/radiance_core/augment_radiance.F90 +atmos_param/socrates/src/trunk/src/radiance_core/def_control.F90 +atmos_param/socrates/src/trunk/src/radiance_core/two_coeff_basic.F90 +atmos_param/socrates/src/trunk/src/radiance_core/sph_solver.F90 +atmos_param/socrates/src/trunk/src/radiance_core/solver_no_scat.F90 +atmos_param/socrates/src/trunk/src/radiance_core/copy_clr_sol.F90 +atmos_param/socrates/src/trunk/src/radiance_core/solar_source.F90 +atmos_param/socrates/src/trunk/src/radiance_core/opt_prop_water_cloud.F90 +atmos_param/socrates/src/trunk/src/radiance_core/legendre_weight.F90 +atmos_param/socrates/src/trunk/src/radiance_core/solve_band_without_gas.F90 +atmos_param/socrates/src/trunk/src/radiance_core/diff_albedo_basis.F90 +atmos_param/socrates/src/trunk/src/radiance_core/increment_rad_cf.F90 +atmos_param/socrates/src/trunk/src/radiance_core/single_scat_sol.F90 +atmos_param/socrates/src/trunk/src/radiance_core/opt_prop_aerosol.F90 +atmos_param/socrates/src/trunk/src/radiance_core/solve_band_ses.F90 +atmos_param/socrates/src/trunk/src/radiance_core/def_ss_prop.F90 +atmos_param/socrates/src/trunk/src/radiance_core/prsc_opt_prop.F90 +atmos_param/socrates/src/trunk/src/radiance_core/layer_part_integ.F90 +atmos_param/socrates/src/trunk/src/radiance_core/two_coeff_fast_lw.F90 +atmos_param/socrates/src/trunk/src/radiance_core/gas_optical_properties.F90 +atmos_param/socrates/src/trunk/src/radiance_core/single_scattering.F90 +atmos_param/socrates/src/trunk/src/radiance_core/monochromatic_ir_radiance.F90 +atmos_param/socrates/src/trunk/src/radiance_core/hemi_sph_integ.F90 +atmos_param/socrates/src/trunk/src/radiance_core/two_coeff_cloud.F90 +atmos_param/socrates/src/trunk/src/radiance_core/read_spectrum.F90 +atmos_param/socrates/src/trunk/src/radiance_core/inter_t_lookup.F90 +atmos_param/socrates/src/trunk/src/radiance_core/solver_triple.F90 +atmos_param/socrates/src/trunk/src/radiance_core/two_stream.F90 +atmos_param/socrates/src/trunk/src/radiance_core/solver_triple_hogan.F90 +atmos_param/socrates/src/trunk/src/radiance_core/monochromatic_radiance_tseq.F90 +atmos_param/socrates/src/trunk/src/radiance_core/cloud_maxcs_split.F90 +atmos_param/socrates/src/trunk/src/radiance_core/def_atm.F90 +atmos_param/socrates/src/trunk/src/radiance_core/two_coeff.F90 +atmos_param/socrates/src/trunk/src/radiance_core/two_coeff_region.F90 +atmos_param/socrates/src/trunk/src/radiance_core/quicksort.F90 +atmos_param/socrates/src/trunk/src/radiance_core/rescale_tau_omega.F90 +atmos_param/socrates/src/trunk/src/radiance_core/calc_radiance_ipa.F90 +atmos_param/socrates/src/trunk/src/radiance_core/two_coeff_region_fast_lw.F90 +atmos_param/socrates/src/trunk/src/radiance_core/diff_planck_source_poly.F90 +atmos_param/socrates/src/trunk/src/radiance_core/set_truncation.F90 +atmos_param/socrates/src/trunk/src/radiance_core/monochromatic_radiance.F90 +atmos_param/socrates/src/trunk/src/radiance_core/aggregate_cloud.F90 +atmos_param/socrates/src/trunk/src/radiance_core/radiance_calc.F90 +atmos_param/socrates/src/trunk/src/radiance_core/ir_source.F90 +atmos_param/socrates/src/trunk/src/scatter/conjugate_gradient_cloud_90.f90 +atmos_param/socrates/src/trunk/src/scatter/method_weight_pcf.f90 +atmos_param/socrates/src/trunk/src/scatter/select_weight_scatter_90.f90 +atmos_param/socrates/src/trunk/src/scatter/measure_particle_pcf.f90 +atmos_param/socrates/src/trunk/src/scatter/prec_integral_tcf.f90 +atmos_param/socrates/src/trunk/src/scatter/def_s_scat_prop.f90 +atmos_param/socrates/src/trunk/src/scatter/db_scatter_integral.f90 +atmos_param/socrates/src/trunk/src/scatter/weightings_90.f90 +atmos_param/socrates/src/trunk/src/scatter/def_db_ss_mono.f90 +atmos_param/socrates/src/trunk/src/scatter/cloud_fitting.f90 +atmos_param/socrates/src/trunk/src/scatter/shape_particle_pcf.f90 +atmos_param/socrates/src/trunk/src/scatter/number_particle_90.f90 +atmos_param/socrates/src/trunk/src/scatter/db_interp_ss_mono.f90 +atmos_param/socrates/src/trunk/src/scatter/db_read_single_wavelength.f90 +atmos_param/socrates/src/trunk/src/scatter/get_refract_index.f90 +atmos_param/socrates/src/trunk/src/scatter/read_scatter_block_90.f90 +atmos_param/socrates/src/trunk/src/scatter/proj_area_particle.f90 +atmos_param/socrates/src/trunk/src/scatter/line_search_cloud_90.f90 +atmos_param/socrates/src/trunk/src/scatter/volume_particle.f90 +atmos_param/socrates/src/trunk/src/scatter/distribution_pcf.f90 +atmos_param/socrates/src/trunk/src/scatter/size_integral_90.f90 +atmos_param/socrates/src/trunk/src/scatter/particle_size_90.f90 +atmos_param/socrates/src/trunk/src/scatter/max_size_acf.f90 +atmos_param/socrates/src/trunk/src/scatter/write_average_90.f90 +atmos_param/socrates/src/trunk/src/scatter/adt_mitchell96.f90 +atmos_param/socrates/src/trunk/src/scatter/get_db_wavelengths.f90 +atmos_param/socrates/src/trunk/src/scatter/decompose_phf_90.f90 +atmos_param/socrates/src/trunk/src/scatter/cloud_fit_parm_acf.f90 +atmos_param/socrates/src/trunk/src/scatter/cloud_fit_90.f90 +atmos_param/socrates/src/trunk/src/scatter/ice_db_read_geometry.f90 +atmos_param/socrates/src/trunk/src/scatter/def_sct_db.f90 +atmos_param/socrates/src/trunk/src/scatter/open_average_90.f90 +atmos_param/socrates/src/trunk/src/scatter/def_size_dist.f90 +atmos_param/socrates/src/trunk/src/scatter/parm_integ_acf.f90 +atmos_param/socrates/src/trunk/src/scatter/bna_factor_ccf.f90 +atmos_param/socrates/src/trunk/src/scatter/def_db_crystal_geometry.f90 +atmos_param/socrates/src/trunk/src/scatter/get_wavelengths.f90 +atmos_param/socrates/src/trunk/src/scatter/weightings_single_90.f90 +atmos_param/socrates/src/trunk/src/scatter/scatter_algorithm_pcf.f90 +atmos_param/socrates/src/trunk/src/scatter/db_type_ucf.f90 +atmos_param/socrates/src/trunk/src/scatter/scatter_integral_90.f90 +atmos_param/socrates/src/trunk/src/correlated_k/voigt_profile.f90 +atmos_param/socrates/src/trunk/src/correlated_k/adjust_path.f90 +atmos_param/socrates/src/trunk/src/correlated_k/func_scale_90.f90 +atmos_param/socrates/src/trunk/src/correlated_k/read_pt_line_90.f90 +atmos_param/socrates/src/trunk/src/correlated_k/set_condition_ck_90.f90 +atmos_param/socrates/src/trunk/src/correlated_k/caviar_continuum_v1_0.f90 +atmos_param/socrates/src/trunk/src/correlated_k/rad_weight_90.f90 +atmos_param/socrates/src/trunk/src/correlated_k/select_weight_ck_90.f90 +atmos_param/socrates/src/trunk/src/correlated_k/trans_k_dist.f90 +atmos_param/socrates/src/trunk/src/correlated_k/type_residual_pcf.f90 +atmos_param/socrates/src/trunk/src/correlated_k/map_shell.f90 +atmos_param/socrates/src/trunk/src/correlated_k/terminate_scale_90.f90 +atmos_param/socrates/src/trunk/src/correlated_k/ckd_continuum_v2_4.f90 +atmos_param/socrates/src/trunk/src/correlated_k/write_fit_90.f90 +atmos_param/socrates/src/trunk/src/correlated_k/ck_parm_acf.f90 +atmos_param/socrates/src/trunk/src/correlated_k/fit_parabola_90.f90 +atmos_param/socrates/src/trunk/src/correlated_k/func_scale_derivative_90.f90 +atmos_param/socrates/src/trunk/src/correlated_k/optimal_k.f90 +atmos_param/socrates/src/trunk/src/correlated_k/open_file_out_90.f90 +atmos_param/socrates/src/trunk/src/correlated_k/residual_gradient_90.f90 +atmos_param/socrates/src/trunk/src/correlated_k/scale_ck_fit_90.f90 +atmos_param/socrates/src/trunk/src/correlated_k/read_ref_pt_90.f90 +atmos_param/socrates/src/trunk/src/correlated_k/corr_k_single.f90 +atmos_param/socrates/src/trunk/src/correlated_k/planck_90.f90 +atmos_param/socrates/src/trunk/src/correlated_k/exponent_fit_90.f90 +atmos_param/socrates/src/trunk/src/correlated_k/conjugate_gradient_90.f90 +atmos_param/socrates/src/trunk/src/correlated_k/line_prof_corr_mod.f90 +atmos_param/socrates/src/trunk/src/correlated_k/set_extern_ckd_frn_data.f90 +atmos_param/socrates/src/trunk/src/correlated_k/set_g_point_90.f90 +atmos_param/socrates/src/trunk/src/correlated_k/bi_interp.f90 +atmos_param/socrates/src/trunk/src/correlated_k/residual_trans_90.f90 +atmos_param/socrates/src/trunk/src/correlated_k/set_extern_ckd_self_data.f90 +atmos_param/socrates/src/trunk/src/correlated_k/line_search_90.f90 +atmos_param/socrates/src/trunk/src/correlated_k/read_hitran.f90 +atmos_param/socrates/src/trunk/src/correlated_k/read_nc.f90 +atmos_param/socrates/src/trunk/src/correlated_k/ckd_extern_data.f90 +atmos_param/socrates/src/trunk/src/correlated_k/def_hitran_record.f90 +atmos_param/socrates/src/trunk/src/correlated_k/d_planck_90.f90 +atmos_param/socrates/src/trunk/src/correlated_k/scale_parameters_acf.f90 +atmos_param/socrates/src/trunk/src/correlated_k/offset_residual_trans_acf.f90 +atmos_param/socrates/src/trunk/src/correlated_k/hitran_cnst.f90 +atmos_param/socrates/src/trunk/src/correlated_k/ck_fit_pcf.f90 +atmos_param/socrates/src/trunk/src/aux/qsat_wat.F90 +atmos_param/socrates/src/trunk/src/aux/qsat_gill.F90 +atmos_param/socrates/src/trunk/src/aux/write_cdf.f90 +atmos_param/socrates/src/trunk/src/aux/read_cdf.f90 +atmos_param/socrates/src/trunk/src/aux/aerosol_representation_pcf.f90 +atmos_param/socrates/src/trunk/src/aux/aerosol_profile_pcf.f90 +atmos_param/socrates/src/trunk/src/aux/write_samson.f90 +atmos_param/socrates/src/trunk/src/aux/filter_function.f90 +atmos_param/socrates/src/trunk/src/aux/qsat_alg_pcf.f90 +atmos_param/socrates/src/trunk/src/aux/method_merge_pcf.f90 +atmos_param/socrates/src/trunk/src/aux/rand_gauss.f90 +atmos_param/socrates/src/trunk/src/general/make_block_2_1.f90 +atmos_param/socrates/src/trunk/src/general/make_block_11.f90 +atmos_param/socrates/src/trunk/src/general/make_block_12.f90 +atmos_param/socrates/src/trunk/src/general/sum_unity.f90 +atmos_param/socrates/src/trunk/src/general/trapezoid_90.f90 +atmos_param/socrates/src/trunk/src/general/get_free_unit.F90 +atmos_param/socrates/src/trunk/src/general/rayleigh_scatter.f90 +atmos_param/socrates/src/trunk/src/general/make_block_18.f90 +atmos_param/socrates/src/trunk/src/general/remove_negative_gas_90.f90 +atmos_param/socrates/src/trunk/src/general/make_block_17.f90 +atmos_param/socrates/src/trunk/src/general/make_block_1.f90 +atmos_param/socrates/src/trunk/src/general/solar_intensity_90.f90 +atmos_param/socrates/src/trunk/src/general/make_block_19.f90 +atmos_param/socrates/src/trunk/src/general/make_block_0.f90 +atmos_param/socrates/src/trunk/src/general/make_block_10.f90 +atmos_param/socrates/src/trunk/src/general/make_block_14.f90 +atmos_param/socrates/src/trunk/src/general/read_solar_spectrum.f90 +atmos_param/socrates/src/trunk/src/general/remove_negative_cont_90.f90 +atmos_param/socrates/src/trunk/src/general/make_block_3_1.f90 +atmos_param/socrates/src/trunk/src/general/set_interactive.f90 +atmos_param/socrates/src/trunk/src/general/make_block_8.f90 +atmos_param/socrates/src/trunk/src/general/make_block_3.f90 +atmos_param/socrates/src/trunk/src/general/make_block_15.f90 +atmos_param/socrates/src/trunk/src/general/make_block_9.f90 +atmos_param/socrates/src/trunk/src/general/read_instrument_response_90.f90 +atmos_param/socrates/src/trunk/src/general/make_block_4.f90 +atmos_param/socrates/src/trunk/src/general/map_heap_func.f90 +atmos_param/socrates/src/trunk/src/general/make_block_6.f90 +atmos_param/socrates/src/trunk/src/general/out_spectrum.f90 +atmos_param/socrates/src/trunk/src/general/make_block_2.f90 +atmos_param/socrates/src/trunk/src/general/rayleigh_jeans_tail.f90 +atmos_param/socrates/src/trunk/src/general/solar_intensity.f90 +atmos_param/socrates/src/trunk/src/general/make_block_5.f90 +atmos_param/socrates/src/trunk/src/general/rayleigh_scatter_h2he.f90 +atmos_param/socrates/src/trunk/src/modules_gen/dimensions_field_cdf_ucf.f90 +atmos_param/socrates/src/trunk/src/modules_gen/def_refract.f90 +atmos_param/socrates/src/trunk/src/modules_gen/scatter_pp_pcf.f90 +atmos_param/socrates/src/trunk/src/modules_gen/def_std_io_icf.f90 +atmos_param/socrates/src/trunk/src/modules_gen/dimensions_cdl_ucf.f90 +atmos_param/socrates/src/trunk/src/modules_gen/refract_re_ccf.f90 +atmos_param/socrates/src/trunk/src/modules_gen/def_inst_flt.f90 +atmos_param/socrates/src/trunk/src/modules_gen/dimensions_pp_ucf.f90 +atmos_param/socrates/src/trunk/src/modules_gen/weighting_pcf.f90 +atmos_param/socrates/src/trunk/src/modules_gen/error_pcf.f90 +atmos_param/socrates/src/trunk/src/modules_gen/file_type_pcf.f90 +atmos_param/socrates/src/trunk/src/modules_gen/def_data_in_icf.f90 +atmos_param/socrates/src/trunk/src/modules_gen/dimensions_cdf_ucf.f90 +atmos_param/socrates/src/trunk/src/modules_gen/unit_list_pcf.f90 +atmos_param/socrates/src/trunk/src/modules_gen/realtypefx_rd.f90 +atmos_param/socrates/src/trunk/src/modules_gen/def_solarspec.f90 +atmos_param/socrates/src/trunk/src/modules_gen/interp_mode_pcf.f90 +atmos_param/socrates/src/trunk/src/modules_gen/dimensions_fixed_pcf.f90 +atmos_param/socrates/src/trunk/src/modules_gen/dimensions_field_ucf.f90 +atmos_param/socrates/src/trunk/src/modules_gen/input_head_pcf.f90 +atmos_param/socrates/src/trunk/src/radiation_control/close_cloud_gen.F90 +atmos_param/socrates/src/trunk/src/radiation_control/mcica_order.F90 +atmos_param/socrates/src/trunk/src/radiation_control/rand_no_mcica.F90 +atmos_param/socrates/src/trunk/src/radiation_control/set_moist_aerosol_properties.F90 +atmos_param/socrates/src/trunk/src/radiation_control/mcica_mod.F90 +atmos_param/socrates/src/trunk/src/radiation_control/cld_generator_mod.F90 +atmos_param/socrates/src/trunk/src/radiation_control/open_cloud_gen.F90 +atmos_param/socrates/src/trunk/src/um/out_nml.f90 +atmos_param/socrates/src/trunk/src/um/def_um_nml.f90 +atmos_param/socrates/src/trunk/src/modules_core/errormessagelength_mod.F90 +atmos_param/socrates/src/trunk/src/modules_core/dimensions_spec_ucf.F90 +atmos_param/socrates/src/trunk/src/modules_core/rad_ccf.F90 +atmos_param/socrates/src/trunk/src/modules_core/yomhook.F90 +atmos_param/socrates/src/trunk/src/modules_core/parkind1.F90 +atmos_param/socrates/src/trunk/src/modules_core/file_manager.F90 +atmos_param/socrates/src/trunk/src/modules_core/missing_data_mod.F90 +atmos_param/socrates/src/trunk/src/modules_core/vectlib_mod.F90 +atmos_param/socrates/src/trunk/src/modules_core/ereport_mod.F90 +atmos_param/socrates/src/trunk/src/modules_core/realtype_rd.f90 +atmos_param/socrates/src/trunk/src/modules_core/filenamelength_mod.F90 +atmos_param/socrates/src/trunk/src/scatter/grow_particles.f +atmos_param/socrates/src/trunk/src/scatter/mie_scatter.f +atmos_param/socrates/src/trunk/src/scatter/adt_integral.f +atmos_param/socrates/src/trunk/src/scatter/gamma_fnc.f +atmos_param/socrates/src/trunk/src/scatter/refractive_index.f +atmos_param/socrates/src/trunk/src/aux/output_vert_cdl.f +atmos_param/socrates/src/trunk/src/aux/assign_input_opt_cdf.f +atmos_param/socrates/src/trunk/src/aux/assign_input_ss_cdl.f +atmos_param/socrates/src/trunk/src/aux/split_cdl_line.f +atmos_param/socrates/src/trunk/src/aux/input_cloud_cdf.f +atmos_param/socrates/src/trunk/src/aux/assign_viewing_geom_cdl.f +atmos_param/socrates/src/trunk/src/aux/read_samson_p_field.f +atmos_param/socrates/src/trunk/src/aux/output_opt_profile_cdl.f +atmos_param/socrates/src/trunk/src/aux/input_cloud_cdl.f +atmos_param/socrates/src/trunk/src/aux/output_radiance_cdl.f +atmos_param/socrates/src/trunk/src/aux/fnc_density.f +atmos_param/socrates/src/trunk/src/aux/calc_volume_fraction.f +atmos_param/socrates/src/trunk/src/aux/calc_cdl_stride.f +atmos_param/socrates/src/trunk/src/aux/interp.f +atmos_param/socrates/src/trunk/src/aux/merge_pressure.f +atmos_param/socrates/src/trunk/src/aux/angular_control_cdf.f +atmos_param/socrates/src/trunk/src/aux/extinction_profile.f +atmos_param/socrates/src/trunk/src/aux/read_genln2_flux.f +atmos_param/socrates/src/trunk/src/aux/write_cdl.f +atmos_param/socrates/src/trunk/src/aux/find_var_cdl.f +atmos_param/socrates/src/trunk/src/aux/assign_input_vert_cdf.f +atmos_param/socrates/src/trunk/src/aux/input_aerosol_cdf.f +atmos_param/socrates/src/trunk/src/aux/assign_input_novert_cdl.f +atmos_param/socrates/src/trunk/src/aux/find_dimen_cdl.f +atmos_param/socrates/src/trunk/src/aux/output_flux_cdl.f +atmos_param/socrates/src/trunk/src/aux/assign_input_opt_cdl.f +atmos_param/socrates/src/trunk/src/aux/output_horiz_cdl.f +atmos_param/socrates/src/trunk/src/aux/seaalbedo_driver.f +atmos_param/socrates/src/trunk/src/aux/assign_viewing_geom_cdf.f +atmos_param/socrates/src/trunk/src/aux/angular_control.f +atmos_param/socrates/src/trunk/src/aux/output_vert_cdf.f +atmos_param/socrates/src/trunk/src/aux/output_view_cdl.f +atmos_param/socrates/src/trunk/src/aux/read_raw_profile.f +atmos_param/socrates/src/trunk/src/aux/qsat_gg_ice.f +atmos_param/socrates/src/trunk/src/aux/output_flux_cdf.f +atmos_param/socrates/src/trunk/src/aux/assign_surface_char_cdf.f +atmos_param/socrates/src/trunk/src/aux/name_length.f +atmos_param/socrates/src/trunk/src/aux/sort_raw_profile.f +atmos_param/socrates/src/trunk/src/aux/read_averaged_scatter.f +atmos_param/socrates/src/trunk/src/aux/write_cdl_field.f +atmos_param/socrates/src/trunk/src/aux/planck_ss_source.f +atmos_param/socrates/src/trunk/src/aux/output_photolysis_cdf.f +atmos_param/socrates/src/trunk/src/aux/output_radiance_cdf.f +atmos_param/socrates/src/trunk/src/aux/interpolate_p.f +atmos_param/socrates/src/trunk/src/aux/write_profile.f +atmos_param/socrates/src/trunk/src/aux/assign_surface_char_cdl.f +atmos_param/socrates/src/trunk/src/aux/planck_cumul.f +atmos_param/socrates/src/trunk/src/aux/input_aerosol_cdl.f +atmos_param/socrates/src/trunk/src/aux/assign_input_novert_cdf.f +atmos_param/socrates/src/trunk/src/aux/output_surf_cdl.f +atmos_param/socrates/src/trunk/src/aux/mono_rad_ss.f +atmos_param/socrates/src/trunk/src/aux/assign_horiz_cdl.f +atmos_param/socrates/src/trunk/src/aux/qsat.f +atmos_param/socrates/src/trunk/src/aux/set_state.f +atmos_param/socrates/src/trunk/src/aux/l_find_component.f +atmos_param/socrates/src/trunk/src/aux/qsat_gg.f +atmos_param/socrates/src/trunk/src/aux/read_cdl.f +atmos_param/socrates/src/trunk/src/aux/assign_input_vert_cdl.f +atmos_param/socrates/src/trunk/src/aux/output_photolysis_cdl.f +atmos_param/socrates/src/trunk/src/general/non_blank.f +atmos_param/socrates/src/trunk/src/general/remove_blank.f +atmos_param/socrates/src/trunk/src/general/simpsons_rule.f +atmos_param/socrates/src/trunk/src/general/point_bracket.f +atmos_param/socrates/src/trunk/src/general/trapezoid.f +atmos_param/socrates/src/trunk/src/general/planck.f +atmos_param/socrates/src/trunk/src/general/read_line.f +atmos_param/socrates/src/trunk/src/general/make_block_6_1.f +atmos_param/socrates/src/trunk/src/general/calc_thermal_coeff.f +atmos_param/socrates/src/trunk/src/general/open_file_out.f +atmos_param/socrates/src/trunk/src/general/back_substitute.f +atmos_param/socrates/src/trunk/src/general/open_file_in.f +atmos_param/socrates/src/trunk/src/general/integrate_spline.f +atmos_param/socrates/src/trunk/src/general/rayleigh_scatter_air.f +atmos_param/socrates/src/trunk/src/general/lock_code.f +atmos_param/socrates/src/trunk/src/general/make_block_6_2.f +atmos_param/socrates/src/trunk/src/general/inner_bracket.f +atmos_param/socrates/src/trunk/src/general/svd_decompose.f +atmos_param/socrates/src/trunk/src/general/calc_planck_tbl.f +atmos_param/socrates/src/trunk/src/general/read_word.f +atmos_param/socrates/interface/read_control.F90 +atmos_param/socrates/interface/set_atm.F90 +atmos_param/socrates/interface/set_cld.F90 +atmos_param/socrates/interface/socrates_set_cld.F90 +atmos_param/socrates/interface/set_dimen.F90 +atmos_param/socrates/interface/socrates_config_mod.f90 +atmos_param/socrates/interface/compress_spectrum.F90 +atmos_param/socrates/interface/set_aer.F90 +atmos_param/socrates/interface/set_bound.F90 +atmos_param/socrates/interface/set_control.F90 +atmos_param/socrates/interface/socrates_calc.F90 +atmos_param/socrates/interface/socrates_interface.F90 +atmos_param/socrates/interface/soc_constants.f90 diff --git a/src/extra/python/isca/__init__.py b/src/extra/python/isca/__init__.py index 5bf28c1a9..830b10774 100644 --- a/src/extra/python/isca/__init__.py +++ b/src/extra/python/isca/__init__.py @@ -83,4 +83,4 @@ def emit(self, event, *args, **kwargs): from isca.experiment import Experiment, DiagTable, Namelist, FailedRunError -from isca.codebase import IscaCodeBase, SocratesCodeBase, DryCodeBase, GreyCodeBase #, ShallowCodeBase +from isca.codebase import IscaCodeBase, SocratesCodeBase, DryCodeBase, GreyCodeBase, ShallowCodeBase, BarotropicCodeBase, ColumnCodeBase, SocColumnCodeBase diff --git a/src/extra/python/isca/codebase.py b/src/extra/python/isca/codebase.py index e6703813a..c411ff928 100644 --- a/src/extra/python/isca/codebase.py +++ b/src/extra/python/isca/codebase.py @@ -7,8 +7,9 @@ from isca import GFDL_WORK, GFDL_BASE, GFDL_SOC, _module_directory, get_env_file from .loghandler import Logger -from .helpers import url_to_folder, destructive, useworkdir, mkdir, cd, git, P, git_run_in_directory +from .helpers import url_to_folder, destructive, useworkdir, mkdir, git, P, git_run_in_directory, check_for_sh_stdout +import pdb class CodeBase(Logger): """The CodeBase. @@ -91,6 +92,28 @@ def __init__(self, repo=None, commit=None, directory=None, storedir=P(GFDL_WORK, self.checkout() else: self.link_source_to(directory) + elif self.code_is_available and self.commit is not None: + # problem is that if you try to checkout a specific commit, and it doesn't work, the next time you try it, the above code will only check if code exists, which it will, but it won't be at the correct commit. This will cause problems for e.g. the trip tests. Following code checks if the code that's checked out is the correct commit ID compared to what was asked for, and gives an error if they are different. + commit_at_HEAD_of_repo = self.git_commit.split('"')[1] + commit_desired = self.commit + if len(commit_desired)==len(commit_at_HEAD_of_repo): + commit_to_compare_1 = commit_desired + commit_to_compare_2 = commit_at_HEAD_of_repo + elif len(commit_desired)>len(commit_at_HEAD_of_repo): + commit_to_compare_1 = commit_desired[0:len(commit_at_HEAD_of_repo)] + commit_to_compare_2 = commit_at_HEAD_of_repo + else: + commit_to_compare_1 = commit_desired + commit_to_compare_2 = commit_at_HEAD_of_repo[0:len(commit_desired)] + + if commit_to_compare_1==commit_to_compare_2: + self.log.info('commit requested successfully checked out') + else: + self.log.warn('commit requested is not the commit to be used') + raise NotImplementedError("commit requested %s but commit supplied %s. This happens when you've previously tried to checkout a particular commit, but the commit was not found in the repo supplied. Try removing %s and trying again, making sure to select a repo that contains your desired commit." % (commit_to_compare_1, commit_to_compare_2, self.workdir )) + + + #TODO self.templates = Environment(loader=FileSystemLoader(self.templatedir)) @@ -114,7 +137,7 @@ def is_clean(self): @property def git_commit(self): - return self.git.log('-1', '--format="%H"').stdout.decode('utf8') + return check_for_sh_stdout(self.git.log('-1', '--format="%H"')) # @property # def git_diff(self): @@ -139,11 +162,12 @@ def write_source_control_status(self, outfile): # write out the git commit id of GFDL_BASE file.write("\n\n*---commit hash used for code in GFDL_BASE, including this python module---*:\n") - file.write(gfdl_git.log('-1', '--format="%H"').stdout.decode('utf8')) + gfdl_git_out = check_for_sh_stdout(gfdl_git.log('-1', '--format="%H"')) + file.write(gfdl_git_out) # if there are any uncommited changes in the working directory, # add those to the file too - source_status = self.git.status("-b", "--porcelain").stdout.decode('utf8') + source_status = check_for_sh_stdout(self.git.status("-b", "--porcelain")) # filter the source status for changes in specific files filetypes = ('.f90', '.inc', '.c') source_status = [line for line in source_status.split('\n') @@ -155,7 +179,7 @@ def write_source_control_status(self, outfile): file.write("*---git status output (only f90 and inc files)---*:\n") file.write('\n'.join(source_status)) file.write('\n\n*---git diff output---*\n') - source_diff = self.git.diff('--no-color').stdout.decode('utf8') + source_diff = check_for_sh_stdout(self.git.diff('--no-color')) file.write(source_diff) def read_path_names(self, path_names_file): @@ -324,6 +348,59 @@ def __init__(self, *args, **kwargs): self.disable_rrtm() self.simlink_to_soc_code() +class SocColumnCodeBase(CodeBase): + """Isca without RRTM but with the Met Office radiation scheme, Socrates. THIS VERSION FOR SINGLE COLUMN USE. + """ + #path_names_file = P(_module_directory, 'templates', 'moist_path_names') + name = 'socrates_column' + executable_name = 'soc_column_isca.x' + + def column_model(self): + self.compile_flags.append('-DCOLUMN_MODEL') + self.log.info('USING SINGLE COLUMN MODEL') + + def disable_rrtm(self): + # add no compile flag + self.compile_flags.append('-DRRTM_NO_COMPILE') + self.log.info('RRTM compilation disabled.') + + def simlink_to_soc_code(self): + #Make symlink to socrates source code if one doesn't already exist. + socrates_desired_location = self.codedir+'/src/atmos_param/socrates/src/trunk' + + #First check if socrates is in correct place already + if os.path.exists(socrates_desired_location): + link_correct = os.path.exists(socrates_desired_location+'/src/') + if link_correct: + socrates_code_in_desired_location=True + else: + socrates_code_in_desired_location=False + if os.path.islink(socrates_desired_location): + self.log.info('Socrates source code symlink is in correct place, but is to incorrect location. Trying to correct.') + os.unlink(socrates_desired_location) + else: + self.log.info('Socrates source code is in correct place, but folder structure is wrong. Contents of the folder '+socrates_desired_location+' should include a src folder.') + else: + socrates_code_in_desired_location=False + self.log.info('Socrates source code symlink does not exist. Creating.') + + # If socrates is not in the right place already, then attempt to make symlink to location of code provided by GFDL_SOC + if socrates_code_in_desired_location: + self.log.info('Socrates source code already in correct place. Continuing.') + else: + if GFDL_SOC is not None: + sh.ln('-s', GFDL_SOC, socrates_desired_location) + elif GFDL_SOC is None: + error_mesg = 'Socrates code is required for SocratesCodebase, but source code is not provided in location GFDL_SOC='+ str(GFDL_SOC) + self.log.error(error_mesg) + raise OSError(error_mesg) + + def __init__(self, *args, **kwargs): + super(SocColumnCodeBase, self).__init__(*args, **kwargs) + self.column_model() + self.disable_rrtm() + self.simlink_to_soc_code() + class GreyCodeBase(CodeBase): """The Frierson model. This is the closest to the Frierson model, with moist dynamics and a @@ -352,6 +429,27 @@ def __init__(self, *args, **kwargs): self.disable_rrtm() self.disable_soc() +class ColumnCodeBase(CodeBase): + """This contains code that will allow one to use all model physics in a single column configuration (i.e. without calling the dynamical core) + """ + #path_names_file = P(_module_directory, 'templates', 'moist_path_names') + name = 'column' + executable_name = 'column_isca.x' + + def column_model(self): + self.compile_flags.append('-DCOLUMN_MODEL') + self.log.info('USING SINGLE COLUMN MODEL') + + def disable_soc(self): + # add no compile flag + self.compile_flags.append('-DSOC_NO_COMPILE') + self.log.info('SOCRATES compilations diabled.') + + def __init__(self, *args, **kwargs): + super(ColumnCodeBase, self).__init__(*args, **kwargs) + self.column_model() + self.disable_soc() + class DryCodeBase(GreyCodeBase): """The Held-Suarez model. @@ -366,8 +464,14 @@ class DryCodeBase(GreyCodeBase): -# class ShallowCodeBase(CodeBase): -# """The Shallow Water Equations. -# """ -# name = 'shallow' -# executable_name = 'shallow.x' +class ShallowCodeBase(CodeBase): + """The Shallow Water Equations. + """ + name = 'shallow' + executable_name = 'shallow.x' + +class BarotropicCodeBase(CodeBase): + """The Barotropic vorticity equations. + """ + name = 'barotropic' + executable_name = 'barotropic_isca.x' \ No newline at end of file diff --git a/src/extra/python/isca/experiment.py b/src/extra/python/isca/experiment.py index b41a40668..9495bd178 100755 --- a/src/extra/python/isca/experiment.py +++ b/src/extra/python/isca/experiment.py @@ -47,7 +47,6 @@ class Experiment(Logger, EventEmitter): 'num_fourier': 42, 'num_spherical': 43, }, - 'T21': { 'lon_max': 64, 'lat_max': 32, @@ -144,6 +143,11 @@ def update_namelist(self, new_vals): def write_namelist(self, outdir): namelist_file = P(outdir, 'input.nml') self.log.info('Writing namelist to %r' % namelist_file) + # A fixed column width is added to be a fixed number as most string namelist variables are + # width 256 in Isca, so that width plus some indentation and the namelist parameters own + # name should not exceed 350 characters. Default f90nml value is 72, which is regularly + # too short for some namelist variables where directories are pointed to. + self.namelist.column_width = 350 self.namelist.write(namelist_file) def write_diag_table(self, outdir): diff --git a/src/extra/python/isca/helpers.py b/src/extra/python/isca/helpers.py index b1bc089d8..e17ce48e2 100644 --- a/src/extra/python/isca/helpers.py +++ b/src/extra/python/isca/helpers.py @@ -4,7 +4,7 @@ import sh mkdir = sh.mkdir.bake('-p') -cd = sh.cd +# cd = sh.cd git = sh.git.bake('--no-pager') P = os.path.join @@ -66,11 +66,19 @@ def git_run_in_directory(GFDL_BASE_DIR, dir_in): try: codedir_git = git.bake('-C', GFDL_BASE_DIR) - git_test = codedir_git.log('-1', '--format="%H"').stdout + git_test = check_for_sh_stdout(codedir_git.log('-1', '--format="%H"')) baked_git_fn = git.bake('-C', dir_in) except: codedir_git = git.bake('--git-dir='+GFDL_BASE_DIR+'/.git', '--work-tree='+GFDL_BASE_DIR) - git_test = codedir_git.log('-1', '--format="%H"').stdout + git_test = check_for_sh_stdout(codedir_git.log('-1', '--format="%H"')) baked_git_fn = git.bake('--git-dir='+dir_in+'/.git', '--work-tree='+dir_in) - return baked_git_fn \ No newline at end of file + return baked_git_fn + +def check_for_sh_stdout(input_exp): + """Versions of sh>2.* have started returning str types rather than a sh.RunningCommand type. To distinguish these possibilites, this function looks at the output of a sh expression, and asks for stdout and decodes it only if the type is a sh.RunningCommand.""" + + if type(input_exp)==sh.RunningCommand: + input_exp=input_exp.stdout.decode('utf8') + + return input_exp \ No newline at end of file diff --git a/src/extra/python/isca/templates/mkmf.template.dt2 b/src/extra/python/isca/templates/mkmf.template.dt2 new file mode 100644 index 000000000..2dc8b207c --- /dev/null +++ b/src/extra/python/isca/templates/mkmf.template.dt2 @@ -0,0 +1,28 @@ +# template for the Intel fortran compiler +# typical use with mkmf +# mkmf -t template.ifc -c"-Duse_libMPI -Duse_netCDF" path_names /usr/local/include +CPPFLAGS = -I/usr/local/include +NETCDF_LIBS = `nf-config --fflags --flibs` + +# FFLAGS: +# -fpp: Use the fortran preprocessor +# -stack_temps: Put temporary runtime arrays on the stack, not heap. +# -safe_cray_ptr: Cray pointers don't alias other variables. +# -ftz: Denormal numbers are flushed to zero. +# -assume byterecl: Specifies the units for the OPEN statement as bytes. +# -shared-intel: Load intel libraries dynamically +# -i4: 4 byte integers +# -r8: 8 byte reals +# -g: Generate symbolic debugging info in code +# -O2: Level 2 speed optimisations +# -diag-disable 6843: +# This suppresses the warning: `warning #6843: A dummy argument with an explicit INTENT(OUT) declaration is not given an explicit value.` of which +# there are a lot of instances in the GFDL codebase. +FFLAGS = $(CPPFLAGS) -fpp -stack_temps -safe_cray_ptr -ftz -assume byterecl -shared-intel -i4 -r8 -g -O2 -diag-disable 6843 -mcmodel large +#FFLAGS = $(CPPFLAGS) -fltconsistency -stack_temps -safe_cray_ptr -ftz -shared-intel -assume byterecl -g -O0 -i4 -r8 -check -warn -warn noerrors -debug variable_locations -inline_debug_info -traceback +FC = $(F90) $(NETCDF_LIBS) +LD = $(F90) $(NETCDF_LIBS) +#CC = mpicc + +LDFLAGS = -lnetcdff -lnetcdf -lmpi -shared-intel +CFLAGS = -D__IFC diff --git a/src/extra/python/isca/templates/mkmf.template.maths2 b/src/extra/python/isca/templates/mkmf.template.maths2 new file mode 100755 index 000000000..fa6151185 --- /dev/null +++ b/src/extra/python/isca/templates/mkmf.template.maths2 @@ -0,0 +1,28 @@ +# template for the Intel fortran compiler +# typical use with mkmf +# mkmf -t template.ifc -c"-Duse_libMPI -Duse_netCDF" path_names /usr/local/include +CPPFLAGS = `nc-config --cflags` +NETCDF_LIBS = `nc-config --libs` + +# FFLAGS: +# -fpp: Use the fortran preprocessor +# -stack_temps: Put temporary runtime arrays on the stack, not heap. +# -safe_cray_ptr: Cray pointers don't alias other variables. +# -ftz: Denormal numbers are flushed to zero. +# -assume byterecl: Specifies the units for the OPEN statement as bytes. +# -shared-intel: Load intel libraries dynamically +# -i4: 4 byte integers +# -r8: 8 byte reals +# -g: Generate symbolic debugging info in code +# -O2: Level 2 speed optimisations +# -diag-disable 6843: +# This suppresses the warning: `warning #6843: A dummy argument with an explicit INTENT(OUT) declaration is not given an explicit value.` of which +# there are a lot of instances in the GFDL codebase. +FFLAGS = $(CPPFLAGS) -fpp -stack_temps -safe_cray_ptr -ftz -assume byterecl -shared-intel -i4 -r8 -g -O2 -diag-disable 6843 -mcmodel large +#FFLAGS = $(CPPFLAGS) -fltconsistency -stack_temps -safe_cray_ptr -ftz -shared-intel -assume byterecl -g -O0 -i4 -r8 -check -warn -warn noerrors -debug variable_locations -inline_debug_info -traceback +FC = $(F90) +LD = $(F90) $(NETCDF_LIBS) +#CC = mpicc + +LDFLAGS = -lnetcdff -lnetcdf -lmpi -shared-intel -lhdf5_hl -lhdf5 -lm -lz -lsz -lbz2 -lxml2 -lcurl +CFLAGS = -D__IFC diff --git a/src/extra/python/isca/templates/mkmf.template.ubuntu_conda b/src/extra/python/isca/templates/mkmf.template.ubuntu_conda new file mode 100755 index 000000000..bd3dd5a83 --- /dev/null +++ b/src/extra/python/isca/templates/mkmf.template.ubuntu_conda @@ -0,0 +1,28 @@ +# template for the gfortran compiler +# typical use with mkmf +# mkmf -t template.ifc -c"-Duse_libMPI -Duse_netCDF" path_names /usr/local/include +CPPFLAGS = `nc-config --cflags` +NC_INC=`nc-config --fflags` +NC_LIB=`nc-config --flibs` + +# FFLAGS: +# -cpp: Use the fortran preprocessor +# -ffree-line-length-none -fno-range-check: Allow arbitrarily long lines +# -fcray-pointer: Cray pointers don't alias other variables. +# -ftz: Denormal numbers are flushed to zero. +# -assume byterecl: Specifies the units for the OPEN statement as bytes. +# -shared-intel: Load intel libraries dynamically +# -i4: 4 byte integers +# -fdefault-real-8: 8 byte reals (compatability for some parts of GFDL code) +# -fdefault-double-8: 8 byte doubles (compat. with RRTM) +# -O2: Level 2 speed optimisations + +FFLAGS = $(CPPFLAGS) $(NC_LIB) -cpp -fcray-pointer \ + -O2 -ffree-line-length-none -fno-range-check \ + -fdefault-real-8 -fdefault-double-8 -fallow-invalid-boz -fallow-argument-mismatch + +FC = $(F90) +LD = $(F90) + +LDFLAGS = -lnetcdff -lnetcdf -lmpi +CFLAGS = -D__IFC diff --git a/src/extra/python/requirements.txt b/src/extra/python/requirements.txt index e2c9a6499..722b8d75b 100644 --- a/src/extra/python/requirements.txt +++ b/src/extra/python/requirements.txt @@ -1,7 +1,7 @@ sh jinja2 -git+git://github.com/marshallward/f90nml.git#egg=f90nml +f90nml numpy pandas xarray -tqdm \ No newline at end of file +tqdm diff --git a/src/extra/python/scripts/calendar_calc.py b/src/extra/python/scripts/calendar_calc.py index 2c40f6673..ac3aac37e 100644 --- a/src/extra/python/scripts/calendar_calc.py +++ b/src/extra/python/scripts/calendar_calc.py @@ -1,4 +1,4 @@ -from cftime import utime +import cftime from datetime import datetime from cmip_time import FakeDT import numpy as np @@ -8,9 +8,11 @@ def day_number_to_datetime_array(time_in, calendar_type, units_in): - cdftime = utime(units_in, calendar = calendar_type) + # cdftime = utime(units_in, calendar = calendar_type) - date_out = cdftime.num2date(time_in) + # date_out = cdftime.num2date(time_in) + + date_out = cftime.num2date(time_in, units_in, calendar=calendar_type) return date_out diff --git a/src/extra/python/scripts/create_timeseries.py b/src/extra/python/scripts/create_timeseries.py index 94a4a3998..b18d241d5 100644 --- a/src/extra/python/scripts/create_timeseries.py +++ b/src/extra/python/scripts/create_timeseries.py @@ -107,7 +107,11 @@ def create_time_arr(num_years,is_climatology,time_spacing): return time_arr,day_number,ntime,time_units,time_bounds -def output_to_file(data,lats,lons,latbs,lonbs,p_full,p_half,time_arr,time_units,file_name,variable_name,number_dict, time_bounds=None): +def output_multiple_variables_to_file(data_dict,lats,lons,latbs,lonbs,p_full,p_half,time_arr,time_units,file_name,number_dict, time_bounds=None): + """Default is to accept multiple data variables to put in the file. + Input format in data_dict = {variable_name:variable_array}. + Currently only accepts all 2D fields or all 3D fields. + Could be updated in future to accept a mix.""" output_file = Dataset(file_name, 'w', format='NETCDF3_CLASSIC') @@ -116,6 +120,10 @@ def output_to_file(data,lats,lons,latbs,lonbs,p_full,p_half,time_arr,time_units, else: is_thd=True + if time_arr is None: + add_time = False + else: + add_time = True lat = output_file.createDimension('lat', number_dict['nlat']) lon = output_file.createDimension('lon', number_dict['nlon']) @@ -137,7 +145,8 @@ def output_to_file(data,lats,lons,latbs,lonbs,p_full,p_half,time_arr,time_units, pfull = output_file.createDimension('pfull', number_dict['npfull']) phalf = output_file.createDimension('phalf', number_dict['nphalf']) - time = output_file.createDimension('time', 0) #s Key point is to have the length of the time axis 0, or 'unlimited'. This seems necessary to get the code to run properly. + if add_time: + time = output_file.createDimension('time', 0) #s Key point is to have the length of the time axis 0, or 'unlimited'. This seems necessary to get the code to run properly. latitudes = output_file.createVariable('lat','d',('lat',)) longitudes = output_file.createVariable('lon','d',('lon',)) @@ -146,7 +155,8 @@ def output_to_file(data,lats,lons,latbs,lonbs,p_full,p_half,time_arr,time_units, pfulls = output_file.createVariable('pfull','d',('pfull',)) phalfs = output_file.createVariable('phalf','d',('phalf',)) - times = output_file.createVariable('time','d',('time',)) + if add_time: + times = output_file.createVariable('time','d',('time',)) latitudes.units = 'degrees_N'.encode('utf-8') latitudes.cartesian_axis = 'Y' @@ -179,11 +189,11 @@ def output_to_file(data,lats,lons,latbs,lonbs,p_full,p_half,time_arr,time_units, phalfs.positive = 'down' phalfs.long_name = 'half pressure level' - - times.units = time_units - times.calendar = 'THIRTY_DAY_MONTHS' - times.calendar_type = 'THIRTY_DAY_MONTHS' - times.cartesian_axis = 'T' + if add_time: + times.units = time_units + times.calendar = 'THIRTY_DAY_MONTHS' + times.calendar_type = 'THIRTY_DAY_MONTHS' + times.cartesian_axis = 'T' if time_bounds is not None: @@ -198,12 +208,6 @@ def output_to_file(data,lats,lons,latbs,lonbs,p_full,p_half,time_arr,time_units, times.bounds = 'time_bounds' - - if is_thd: - output_array_netcdf = output_file.createVariable(variable_name,'f4',('time','pfull', 'lat','lon',)) - else: - output_array_netcdf = output_file.createVariable(variable_name,'f4',('time','lat','lon',)) - latitudes[:] = lats longitudes[:] = lons @@ -216,14 +220,34 @@ def output_to_file(data,lats,lons,latbs,lonbs,p_full,p_half,time_arr,time_units, pfulls[:] = p_full phalfs[:] = p_half - if type(time_arr[0])!=np.float64 and type(time_arr[0])!=np.int64 : - times[:] = date2num(time_arr,units='days since 0001-01-01 00:00:00.0',calendar='360_day') - else: - times[:] = time_arr - - output_array_netcdf[:] = data + if add_time: + if type(time_arr[0])!=np.float64 and type(time_arr[0])!=np.int64 : + times[:] = date2num(time_arr,units='days since 0001-01-01 00:00:00.0',calendar='360_day') + else: + times[:] = time_arr + + for variable_name in data_dict.keys(): + if is_thd: + if add_time: + three_d_dims = ('time','pfull', 'lat','lon',) + else: + three_d_dims = ('pfull', 'lat','lon',) + + output_array_netcdf = output_file.createVariable(variable_name,'f4',three_d_dims) + else: + if add_time: + two_d_dims = ('time','lat','lon',) + else: + two_d_dims = ('lat','lon',) + output_array_netcdf = output_file.createVariable(variable_name,'f4',two_d_dims) + + output_array_netcdf[:] = data_dict[variable_name] output_file.close() +def output_to_file(data,lats,lons,latbs,lonbs,p_full,p_half,time_arr,time_units,file_name,variable_name,number_dict, time_bounds=None): + """Special interface for script wanting to output 1 variable only.""" + data_dict_to_send = {variable_name:data} + output_multiple_variables_to_file(data_dict_to_send,lats,lons,latbs,lonbs,p_full,p_half,time_arr,time_units,file_name,number_dict,time_bounds=time_bounds) diff --git a/src/extra/python/scripts/find_namelists_to_check.py b/src/extra/python/scripts/find_namelists_to_check.py new file mode 100644 index 000000000..f13956ada --- /dev/null +++ b/src/extra/python/scripts/find_namelists_to_check.py @@ -0,0 +1,88 @@ +import subprocess +import os +import glob +from pathlib import Path +import pdb + +#A script to find the fortran files within Isca's src directory +#that include namelists, and to check if namelist checking is done. + +#find the location of the source code +GFDL_BASE = os.environ['GFDL_BASE'] + +#setup some output dictionaries and lists +fortran_file_dict = {} +includes_namelist_dict = {} +includes_check_namelist_dict = {} +n_check_namelist_dict = {} +if_def_internal_nml_dict={} + +files_with_namelists = [] +namelists_to_flag = [] +namelists_to_flag_possible = [] + + +#find ALL of the fortran files within GFDL_BASE/src directory +for path in Path(f'{GFDL_BASE}/src/').rglob('*.*90'): + #exclude files with ._ at the start + if path.name[0:2]!='._': + #add all the remaining files to a dictionary + fortran_file_dict[path.name] = path + +#go through each file and check if it contains a namelist, and if it does namelist checking +for file_name in fortran_file_dict.keys(): + file_path = fortran_file_dict[file_name] + + #initialise some of the checking variables + namelist_in_file=False + check_namelist_in_file=False + number_of_checks=0 + if_def_internal_nml_in_file=False + #open each of the fortran files + with open(file_path, 'r') as read_obj: + for line in read_obj: + #check if it contains a namelist + if 'namelist /' in line and not namelist_in_file: + namelist_in_file=True + # does it contain the check_nml_error command? + if 'check_nml_error' in line and not check_namelist_in_file: + check_namelist_in_file=True + # count how many times this string is mentioned + if 'check_nml_error' in line: + number_of_checks=number_of_checks+1 + #check if there's more than one type of namelist reading available + if '#ifdef INTERNAL_FILE_NML' in line and not if_def_internal_nml_in_file: + if_def_internal_nml_in_file=True + + #make a list of those files that do have a namelist + if namelist_in_file: + files_with_namelists.append(file_name) + + #make a list of those files that do have a namelist but don't do checking + if namelist_in_file and not check_namelist_in_file: + namelists_to_flag.append(file_name) + + #making a list of files that have namelists, that read them in more than one way, and have fewer than 3 mentions of check_nml_error. This is to catch cases where there is some namelist checking taking place, but it's not on all the methods of namelist reading. + if namelist_in_file and if_def_internal_nml_in_file and number_of_checks<3: + namelists_to_flag_possible.append(file_name) + + #keep a record of the files that include a namelist + includes_namelist_dict[file_name]=namelist_in_file + + #keep a record of the files that do and don't do namelist checking + includes_check_namelist_dict[file_name]=check_namelist_in_file + + #keep a record of the number of checks taking place + n_check_namelist_dict[file_name] = number_of_checks + +#create a list of files that appear in namelists_to_flag_possible +list_of_filepaths_to_check = [str(fortran_file_dict[path]) for path in namelists_to_flag_possible] + +#print the number of checks +print([n_check_namelist_dict[path] for path in namelists_to_flag_possible]) + +#print the list of files +print(namelists_to_flag_possible) + +#print their directories +print(list_of_filepaths_to_check) \ No newline at end of file diff --git a/src/extra/python/scripts/remove_certain_restart_and_data_files.py b/src/extra/python/scripts/remove_certain_restart_and_data_files.py index 98d658a95..582a05c9c 100644 --- a/src/extra/python/scripts/remove_certain_restart_and_data_files.py +++ b/src/extra/python/scripts/remove_certain_restart_and_data_files.py @@ -1,6 +1,7 @@ import sh import os import pdb +from glob import glob P = os.path.join @@ -12,11 +13,15 @@ def __init__(self, basedir, workdir, datadir, exp_name): self.expname = exp_name -def create_exp_object(exp_name): +def create_exp_object(exp_name, data_directory=None): + + if data_directory is None: + datadir = os.environ['GFDL_DATA'] + else: + datadir = data_directory - basedir = os.environ['GFDL_BASE'] workdir = os.environ['GFDL_WORK'] - datadir = os.environ['GFDL_DATA'] + basedir = os.environ['GFDL_BASE'] expname = '/'+exp_name+'/' exp_object = temporary_exp_object(basedir, workdir, datadir, exp_name) @@ -24,34 +29,42 @@ def create_exp_object(exp_name): return exp_object -def keep_only_certain_restart_files(exp_object, max_num_files, interval=12): +# def keep_only_certain_restart_files(exp_object, max_num_files, interval=12): - # sh.ls(sh.glob(P(self.workdir,'restarts','res_*.cpio'))) #TODO get max_num_files calculated in line, rather than a variable to pass. +# # sh.ls(sh.glob(P(self.workdir,'restarts','res_*.cpio'))) #TODO get max_num_files calculated in line, rather than a variable to pass. - #First defines a list of ALL the restart file numbers - files_to_remove=list(range(0,max_num_files)) +# #First defines a list of ALL the restart file numbers +# files_to_remove=list(range(0,max_num_files)) - #Then defines a list of the ones we want to KEEP - files_to_keep =list(range(0,max_num_files,interval)) +# #Then defines a list of the ones we want to KEEP +# files_to_keep =list(range(0,max_num_files,interval)) - #Then we remove the files we want to keep from the list of all files, giving a list of those we wish to remove - for x in files_to_keep: - files_to_remove.remove(x) +# #Then we remove the files we want to keep from the list of all files, giving a list of those we wish to remove +# for x in files_to_keep: +# files_to_remove.remove(x) - #Then we remove them. - for entry in files_to_remove: - try: - sh.rm(P(exp_object.workdir,exp_object.expname,'restarts','res_'+str(entry)+'.cpio')) -# print P(exp_object.workdir,exp_object.expname,'restarts','res_'+str(entry)+'.cpio') +# #Then we remove them. +# for entry in files_to_remove: +# try: +# sh.rm(P(exp_object.workdir,exp_object.expname,'restarts','res_'+str(entry)+'.cpio')) +# # print P(exp_object.workdir,exp_object.expname,'restarts','res_'+str(entry)+'.cpio') - except sh.ErrorReturnCode_1: - pass -# print 'Tried to remove some restart files, but number '+str(entry)+' does not exist' +# except sh.ErrorReturnCode_1: +# pass +# # print 'Tried to remove some restart files, but number '+str(entry)+' does not exist' -def keep_only_certain_restart_files_data_dir(exp_object, max_num_files, interval=12): +def keep_only_certain_restart_files_data_dir(exp_object, max_num_files=None, interval=12): # sh.ls(sh.glob(P(self.workdir,'restarts','res_*.cpio'))) #TODO get max_num_files calculated in line, rather than a variable to pass. + if max_num_files is None: + month_list = glob(P(exp_object.datadir,exp_object.expname, 'restarts')+'/res*.tar.gz') + if len(month_list)==0: + return + else: + final_month = month_list[-1].split('/res') + max_num_files = int(final_month[-1].split('.tar.gz')[0]) + #First defines a list of ALL the restart file numbers files_to_remove=list(range(0,max_num_files)) @@ -62,13 +75,27 @@ def keep_only_certain_restart_files_data_dir(exp_object, max_num_files, interval for x in files_to_keep: files_to_remove.remove(x) + first_to_be_removed = True + number_removed = 0 + number_not_removed = 0 #Then we remove them. - for entry in files_to_remove: + for entry in files_to_remove[1:-1]: try: - sh.rm(P(exp_object.datadir,exp_object.expname,'run%03d' % entry,'INPUT','res')) -# print 'would be removing ' + P(exp_object.datadir,exp_object.expname,'run'+str(entry),'INPUT','res') + file_to_remove = P(exp_object.datadir,exp_object.expname, 'restarts', 'res%04d.tar.gz' % entry) + if os.path.isfile(file_to_remove) and first_to_be_removed: + first_to_be_removed=False + number_not_removed+=1 + # print('would have removed '+file_to_remove+' but wanted to make sure not to delete the first restart') + else: + sh.rm(file_to_remove) + number_removed+=1 + # print('have removed ' + file_to_remove) except sh.ErrorReturnCode_1: + number_not_removed+=1 + # print('could not remove ' + file_to_remove) pass + print(P(exp_object.datadir,exp_object.expname), 'number removed '+str(number_removed), 'number not removed '+str(number_not_removed)) + # print 'Tried to remove some restart files, but number '+str(entry)+' does not exist' def keep_only_certain_daily_data_uninterp(exp_object, max_num_files, interval=None, file_name = 'atmos_daily.nc'): @@ -88,8 +115,8 @@ def keep_only_certain_daily_data_uninterp(exp_object, max_num_files, interval=No #Then we remove them. for entry in files_to_remove: try: - sh.rm(P(exp_object.datadir,exp_object.expname,'run%03d' % entry,file_name)) - print(('Removed '+P(exp_object.datadir,exp_object.expname,'run%03d' % entry,file_name))) + sh.rm(P(exp_object.datadir,exp_object.expname,'run%04d' % entry,file_name)) + print(('Removed '+P(exp_object.datadir,exp_object.expname,'run%04d' % entry,file_name))) except sh.ErrorReturnCode_1: pass # print 'Tried to remove some atmos_daily files, but number '+str(entry)+' does not exist' @@ -98,36 +125,18 @@ def keep_only_certain_daily_data_uninterp(exp_object, max_num_files, interval=No if __name__=="__main__": - max_num_files_input = 325 + max_num_files_input = None -# exp_name_list=['simple_continents_post_princeton_qflux_anoms_'+str(x) for x in range(31,32)] - -# exp_name_list=['aquaplanet_qflux_anoms_'+str(x) for x in [12,18,23,32,8]] - -# exp_name_list = ['simple_continents_post_princeton_qflux_control_1','simple_continents_post_princeton_fixed_sst_1', 'simple_continents_post_princeton_qflux_control_nod_1', 'simple_continents_post_princeton_qflux_control_scf_1'] -# -# exp_name_list = ['annual_mean_ice_princeton_qflux_control_matrix_qflux_2017_code_1', 'annual_mean_ice_post_princeton_fixed_sst_1', 'annual_mean_ice_princeton_fixed_sst_1'] -# -# exp_name_list.extend(['annual_mean_ice_post_princeton_fixed_sst_el_nino_1']) - -# exp_name_list = ['simple_continents_post_princeton_qflux_control_1'] - -# exp_name_list = ['annual_mean_ice_princeton_qflux_control_1']#, 'annual_mean_ice_post_princeton_qflux_control_1'] - -# exp_name_list = ['annual_mean_ice_post_princeton_fixed_sst_TEST_1', 'annual_mean_ice_princeton_qflux_control_matrix_qflux_1'] - -# exp_name_list.extend(['simple_continents_post_princeton_fixed_sst_1']) - -# exp_name_list = ['giant_drag_exp_chai_values_without_dc_bug_latest_1'] -# exp_name_list = ['aquaplanet_qflux_control_1'] + # exp_name_list = [''] + exp_name_list = glob('/disca/share/sit204/data_isca_from_gv5/frierson_post_soc_fix_*/') - exp_name_list = ['giant_drag_exp_chai_values_with_dc_bug_latest_start_to_finish_1', 'giant_drag_exp_chai_values_without_dc_bug_latest_start_to_finish_1'] for exp_name_input in exp_name_list: - temp_obj = create_exp_object(exp_name_input) - keep_only_certain_restart_files(temp_obj, max_num_files_input) + print('Percentage progress through list:'+str(exp_name_list.index(exp_name_input)/len(exp_name_list))) + temp_obj = create_exp_object(exp_name_input, data_directory='/disca/share/sit204/data_from_isca_cpu/') + # keep_only_certain_restart_files(temp_obj, max_num_files_input) keep_only_certain_restart_files_data_dir(temp_obj, max_num_files_input) - keep_only_certain_daily_data_uninterp(temp_obj, max_num_files_input, file_name = 'fms_moist.x') + # keep_only_certain_daily_data_uninterp(temp_obj, max_num_files_input, file_name = 'fms_moist.x') # keep_only_certain_daily_data_uninterp(temp_obj, max_num_files_input) \ No newline at end of file diff --git a/src/extra/python/scripts/shallow_water_init_conds.py b/src/extra/python/scripts/shallow_water_init_conds.py new file mode 100644 index 000000000..2f8bca068 --- /dev/null +++ b/src/extra/python/scripts/shallow_water_init_conds.py @@ -0,0 +1,184 @@ +# -*- coding: utf-8 -*-s +from typing import NoReturn +import numpy as np +import pdb +import create_timeseries as cts +import xarray as xar +import gauss_grid as gg +import matplotlib.pyplot as plt +import windspharm as wsp +import pdb + +def convert_to_vor_div(u_in, v_in, lat_arr, planet_radius): + """convert spherical polar velocities to vor and div""" + + uwnd, uwnd_info = wsp.tools.prep_data(u_in, 'yx') + vwnd, vwnd_info = wsp.tools.prep_data(v_in, 'yx') + + # It is also required that the latitude dimension is north-to-south. Again the + # bundled tools make this easy. + lat_1d_ordered, uwnd, vwnd = wsp.tools.order_latdim(lat_arr[:,0], uwnd, vwnd) + + # Create a VectorWind instance to handle the computation of streamfunction and + # velocity potential. + w = wsp.standard.VectorWind(uwnd, vwnd, rsphere=planet_radius, gridtype='gaussian') + + # Compute the streamfunction and velocity potential. Also use the bundled + # tools to re-shape the outputs to the 4D shape of the wind components as they + # were read off files. + vor = w.vorticity() + div = w.divergence() + # sf, vp = w.sfvp() + vor = wsp.tools.recover_data(vor, uwnd_info) + div = wsp.tools.recover_data(div, uwnd_info) + + return vor[::-1,:], div[::-1,:] #need to reverse latitude reordering + +def set_u_v_height_field(lon_in, lat_in, lonb_in, latb_in, epsilon, alpha, beta, m, r_0, planet_radius, northern_hemisphere=True): + """Configure an initial condition for u, v and h given some + balance condition. Use parameters and gradient-wind balance for Saturn + from 10.1016/j.icarus.2017.06.006""" + + deformation_scale = 3200e3 #p62 of Rostami et al 2017 + f_0 = 3.2e-4 + timescale = (f_0)**-1 + velocity_scale = deformation_scale/timescale + + lat_rad_2d = np.deg2rad(lat_in) + lon_rad_2d = np.deg2rad(lon_in) + + if northern_hemisphere: + r_array = (planet_radius * (np.pi/2. - lat_rad_2d))/deformation_scale #non-dim + else: + r_array = (planet_radius * (np.pi/2. + lat_rad_2d))/deformation_scale #non-dim + + v = np.zeros_like(lat_in) + u = epsilon * ((r_array - r_0)**alpha)* np.exp(-m*((r_array-r_0)**beta)) + + v_si_units = v * velocity_scale + u_si_units = u * velocity_scale + + if northern_hemisphere: + grad_geopot = ((u_si_units**2)/(r_array* deformation_scale)) + (f_0*np.sin(lat_rad_2d)*u_si_units) + else: + #I've changed the sign of the coriolis term here. Clearly this isn't really happening, but in this funny radial coordinate system the sign of u would change for the opposite hemisphere, thus necessitating the sign change. + grad_geopot = ((u_si_units**2)/(r_array* deformation_scale)) - (f_0*np.sin(lat_rad_2d)*u_si_units) + + geopotential = np.zeros_like(grad_geopot) + + if northern_hemisphere: + for lat_idx in range(1, len(lat_rad_2d[:,0])): + geopotential[lat_idx,:] = geopotential[lat_idx-1,:] + 0.5*(grad_geopot[lat_idx-1,:]+grad_geopot[lat_idx,:])*(r_array[lat_idx]-r_array[lat_idx-1]) + else: + r_array_opposite = r_array[::-1,:] + grad_geopot_opposite = grad_geopot[::-1,:] + for lat_idx in range(1, len(lat_rad_2d[:,0])): + geopotential[lat_idx,:] = geopotential[lat_idx-1,:] + 0.5*(grad_geopot_opposite[lat_idx-1,:]+grad_geopot_opposite[lat_idx,:])*(r_array_opposite[lat_idx]-r_array_opposite[lat_idx-1]) + + geopotential = geopotential[::-1,:] + + #we want to pass a geopotential field that has an area-mean of zero. This is because we want to preserve the mean geopotential that the model sets as its h_0 parameter. + + delta_lat_arr = np.diff(latb_in, axis=0)[:,0:-1] + + area_array = np.cos(np.deg2rad(lat_in))*np.deg2rad(delta_lat_arr) + + area_av_geopot = np.sum(geopotential*area_array)/np.sum(area_array) + + geopotential_av_removed = geopotential-area_av_geopot + + area_av_final = np.sum(geopotential_av_removed*area_array)/np.sum(area_array) + + print(f'old mean = {area_av_geopot}, final area_av geopot = {area_av_final}') + + geopotential_si_units = geopotential_av_removed * deformation_scale + + h_0 = (deformation_scale*f_0)**2. + + return u_si_units, v_si_units, geopotential_si_units, h_0, grad_geopot + +nlat=128 +nlon=256 + +latitudes, latitude_bounds_2 = gg.gaussian_latitudes(int(nlat/2)) +latitude_bounds = [latitude_bound[0] for latitude_bound in latitude_bounds_2] + [latitude_bounds_2[-1][1]] + +longitudes = np.linspace(0., 360., nlon, endpoint=False) +delta_lon = longitudes[1]-longitudes[0] +longitude_bounds = [lon_val-(0.5*delta_lon) for lon_val in longitudes] + [np.max(longitudes)+(0.5*delta_lon)] +time_arr_adj=None + +lon_array_2d, lat_array_2d = np.meshgrid(longitudes, latitudes) +lonb_array_2d, latb_array_2d = np.meshgrid(longitude_bounds, latitude_bounds) + +#Note that in the following we're making the initial condition symmetric about the equator. This is because if you only set the initial conditions in the northern hemisphere then you end up needing a very large set of latitudinal functions to get that level of asymmetry, and the code gets very upset when translating that to a finite spectral representation. Making it symmetric gets rid of this problem, at least to some extent. + +epsilon = 0.15*2. +alpha = 0.42 +beta = 1.3 +r_0 = 0. +m_param = 1. +planet_radius = 55000e3 + +u_array_vortex, v_array_vortex, height_array_vortex, h_0, grad_geopot_vortex = set_u_v_height_field(lon_array_2d, lat_array_2d,lonb_array_2d, latb_array_2d, epsilon, alpha, beta, m_param, r_0, planet_radius) + +u_array_vortex_sp, v_array_vortex_sp, height_array_vortex_sp, h_0_sp, grad_geopot_vortex_sp = set_u_v_height_field(lon_array_2d, lat_array_2d,lonb_array_2d, latb_array_2d, epsilon, alpha, beta, m_param, r_0, planet_radius, northern_hemisphere=False) + +epsilon = 0.08 +alpha = 0. +beta = 2. +r_0 = 3.37 +m_param = 3. +planet_radius = 55000e3 + +u_array_jet, v_array_jet, height_array_jet, h_0, grad_geopot_jet = set_u_v_height_field(lon_array_2d, lat_array_2d,lonb_array_2d, latb_array_2d, epsilon, alpha, beta, m_param, r_0, planet_radius) + +u_array_jet_sp, v_array_jet_sp, height_array_jet_sp, h_0_sp, grad_geopot_jet_sp = set_u_v_height_field(lon_array_2d, lat_array_2d,lonb_array_2d, latb_array_2d, epsilon, alpha, beta, m_param, r_0, planet_radius, northern_hemisphere=False) + + +u_array_total = u_array_vortex+u_array_vortex_sp + u_array_jet+u_array_jet_sp +v_array_total = v_array_vortex+v_array_vortex_sp + v_array_jet+v_array_jet_sp +height_array_total = height_array_vortex+height_array_vortex_sp + height_array_jet+height_array_jet_sp +grad_geopot_total = grad_geopot_vortex + grad_geopot_vortex_sp + grad_geopot_jet + grad_geopot_jet_sp + +vor_array, div_array = convert_to_vor_div(u_array_total, v_array_total, height_array_total, planet_radius) + + +p_full=None +p_half=None + +npfull=None +nphalf=None + +#Output it to a netcdf file. + +file_name='rostami_t85_jet_and_vortex_mk7_gg.nc' + + +number_dict={} +number_dict['nlat']=nlat +number_dict['nlon']=nlon +number_dict['nlatb']=nlat+1 +number_dict['nlonb']=nlon+1 +number_dict['npfull']=npfull +number_dict['nphalf']=nphalf +number_dict['ntime']=None + +data_dict = { + 'vor': vor_array, + 'height': height_array_total, + 'div': div_array, + 'ucomp': u_array_total, + 'vcomp': v_array_total, + 'grad_geopot': grad_geopot_total +} + + +time_units=None + +cts.output_multiple_variables_to_file(data_dict,latitudes,longitudes,latitude_bounds,longitude_bounds,p_full,p_half,time_arr_adj,time_units,file_name,number_dict) + +print(f'Must set h_0 parameter in code to be {h_0}') + + + diff --git a/src/shared/astronomy/astronomy.f90 b/src/shared/astronomy/astronomy.f90 index b5181b56d..e3e88854e 100644 --- a/src/shared/astronomy/astronomy.f90 +++ b/src/shared/astronomy/astronomy.f90 @@ -296,6 +296,7 @@ subroutine astronomy_init (latb, lonb) !----------------------------------------------------------------------- #ifdef INTERNAL_FILE_NML read (input_nml_file, astronomy_nml, iostat=io) + ierr = check_nml_error(io,'astronomy_nml') #else if ( file_exist('input.nml')) then unit = open_namelist_file ( ) diff --git a/src/shared/axis_utils/axis_utils.F90 b/src/shared/axis_utils/axis_utils.F90 index c83e35be9..461029193 100644 --- a/src/shared/axis_utils/axis_utils.F90 +++ b/src/shared/axis_utils/axis_utils.F90 @@ -854,6 +854,7 @@ program test !---reading namelist #ifdef INTERNAL_FILE_NML read (input_nml_file, test_axis_utils_nml, iostat=io) + ierr = check_nml_error(io,'test_axis_utils_nml') ! also initializes nml error codes #else if(file_exist('input.nml')) then unit = open_namelist_file() diff --git a/src/shared/constants/constants.F90 b/src/shared/constants/constants.F90 index 130d3c7f7..42ee900a5 100644 --- a/src/shared/constants/constants.F90 +++ b/src/shared/constants/constants.F90 @@ -58,16 +58,16 @@ module constants_mod ! ! acceleration due to gravity ! -! +! ! gas constant for dry air ! ! ! RDGAS / CP_AIR ! -! +! ! specific heat capacity of dry air at constant pressure ! -! +! ! specific heat capacity taken from McDougall (2002) "Potential Enthalpy ..." ! ! @@ -76,8 +76,8 @@ module constants_mod ! ! reciprocal of average density of sea water ! -! -! (kg/m^3)*(cal/kg/deg C)(joules/cal) = (joules/m^3/deg C) +! +! (kg/m^3)*(J/kg/K) = (J/m^3/K) ! real, public, parameter :: EARTH_GRAV = 9.80 @@ -90,7 +90,7 @@ module constants_mod real, public, parameter :: RHO_CP = RHO0*CP_OCEAN !------------ water vapor constants --------------- -! +! ! Humidity factor. Controls the humidity content of the atmosphere through ! the Saturation Vapour Pressure expression when using DO_SIMPLE. ! @@ -116,7 +116,7 @@ module constants_mod ! temp where fresh water freezes ! -real, public, parameter :: ES0 = 1.0 +real, public, parameter :: DEF_ES0 = 1.0 real, public, parameter :: RVGAS = 461.50 real, public, parameter :: CP_VAPOR = 4.0*RVGAS real, public, parameter :: DENS_H2O = 1000. @@ -264,9 +264,10 @@ module constants_mod real, public :: RDGAS = EARTH_RDGAS real, public :: KAPPA = EARTH_KAPPA real, public :: CP_AIR = EARTH_CP_AIR +real, public :: es0 = DEF_ES0 logical :: earthday_multiple = .false. -namelist/constants_nml/ radius, grav, omega, orbital_period, pstd, pstd_mks, rdgas, kappa, solar_const, earthday_multiple +namelist/constants_nml/ radius, grav, omega, orbital_period, pstd, pstd_mks, rdgas, kappa, solar_const, earthday_multiple, es0 !----------------------------------------------------------------------- ! version and tagname published @@ -311,7 +312,7 @@ subroutine constants_init else seconds_per_sol = abs(2*pi / (orbital_rate - omega)) endif - + CP_AIR = RDGAS/KAPPA constants_initialised = .true. @@ -352,4 +353,3 @@ end module constants_mod ! ! - diff --git a/src/shared/diag_manager/diag_table.F90 b/src/shared/diag_manager/diag_table.F90 index 1cb063bef..650ac020e 100644 --- a/src/shared/diag_manager/diag_table.F90 +++ b/src/shared/diag_manager/diag_table.F90 @@ -357,7 +357,7 @@ SUBROUTINE parse_diag_table(diag_subset, istat, err_msg) INTEGER, INTENT(out), OPTIONAL, TARGET :: istat CHARACTER(len=*), INTENT(out), OPTIONAL :: err_msg - INTEGER, PARAMETER :: DT_LINE_LENGTH = 256 + INTEGER, PARAMETER :: DT_LINE_LENGTH = 512 INTEGER :: stdlog_unit !< Fortran file unit number for the stdlog file. INTEGER :: record_len !< String length of the diag_table line read in. diff --git a/src/shared/horiz_interp/horiz_interp.F90 b/src/shared/horiz_interp/horiz_interp.F90 index 9a5263a05..a54aa34ca 100644 --- a/src/shared/horiz_interp/horiz_interp.F90 +++ b/src/shared/horiz_interp/horiz_interp.F90 @@ -1288,6 +1288,7 @@ program horiz_interp_test !--- read namelist #ifdef INTERNAL_FILE_NML read (input_nml_file, test_horiz_interp_nml, iostat=io) + ierr = check_nml_error(io, 'test_horiz_interp_nml') #else if (file_exist('input.nml')) then ierr=1 diff --git a/src/shared/horiz_interp/horiz_interp_spherical.F90 b/src/shared/horiz_interp/horiz_interp_spherical.F90 index 5c52909f7..6603650c8 100644 --- a/src/shared/horiz_interp/horiz_interp_spherical.F90 +++ b/src/shared/horiz_interp/horiz_interp_spherical.F90 @@ -104,6 +104,7 @@ subroutine horiz_interp_spherical_init call write_version_number (version, tagname) #ifdef INTERNAL_FILE_NML read (input_nml_file, horiz_interp_spherical_nml, iostat=io) + ierr = check_nml_error(io,'horiz_interp_spherical_nml') ! also initializes nml error codes #else if (file_exist('input.nml')) then unit = open_namelist_file ( ) diff --git a/src/shared/mpp/mpp.F90 b/src/shared/mpp/mpp.F90 index 0c808bac3..3f69032b1 100644 --- a/src/shared/mpp/mpp.F90 +++ b/src/shared/mpp/mpp.F90 @@ -1182,7 +1182,7 @@ module mpp_mod ! variables needed for subroutine read_input_nml (include/mpp_util.inc) ! ! parameter defining length of character variables - integer, parameter :: INPUT_STR_LENGTH = 256 + integer, parameter :: INPUT_STR_LENGTH = 512 ! public variable needed for reading input.nml from an internal file character(len=INPUT_STR_LENGTH), dimension(:), allocatable, public :: input_nml_file !*********************************************************************** diff --git a/src/shared/sat_vapor_pres/sat_vapor_pres.F90 b/src/shared/sat_vapor_pres/sat_vapor_pres.F90 index 1e1a6f3aa..f182831a6 100644 --- a/src/shared/sat_vapor_pres/sat_vapor_pres.F90 +++ b/src/shared/sat_vapor_pres/sat_vapor_pres.F90 @@ -2278,6 +2278,7 @@ subroutine sat_vapor_pres_init(err_msg) !---- read namelist input ---- #ifdef INTERNAL_FILE_NML read (input_nml_file, sat_vapor_pres_nml, iostat=io) + ierr = check_nml_error(io,'sat_vapor_pres_nml') #else if (file_exist('input.nml')) then unit = open_namelist_file ( ) diff --git a/src/shared/time_interp/time_interp.F90 b/src/shared/time_interp/time_interp.F90 index 94d7046db..442beb020 100644 --- a/src/shared/time_interp/time_interp.F90 +++ b/src/shared/time_interp/time_interp.F90 @@ -233,6 +233,7 @@ subroutine time_interp_init() #ifdef INTERNAL_FILE_NML read (input_nml_file, time_interp_nml, iostat=io) + ierr = check_nml_error (io, 'time_interp_nml') #else namelist_unit = open_namelist_file() ierr=1 diff --git a/src/shared/time_manager/get_cal_time.F90 b/src/shared/time_manager/get_cal_time.F90 index 912686a57..dc36d084f 100644 --- a/src/shared/time_manager/get_cal_time.F90 +++ b/src/shared/time_manager/get_cal_time.F90 @@ -188,6 +188,7 @@ function get_cal_time(time_increment, units, calendar, permit_calendar_conversio if(.not.module_is_initialized) then #ifdef INTERNAL_FILE_NML read (input_nml_file, get_cal_time_nml, iostat=io) + ierr = check_nml_error (io, 'get_cal_time_nml') #else namelist_unit = open_namelist_file() ierr=1 diff --git a/src/shared/time_manager/time_manager.F90 b/src/shared/time_manager/time_manager.F90 index 8d78b0ae8..6f7654f26 100644 --- a/src/shared/time_manager/time_manager.F90 +++ b/src/shared/time_manager/time_manager.F90 @@ -3479,6 +3479,7 @@ program test #ifdef INTERNAL_FILE_NML read (input_nml_file, test_nml, iostat=io) + ierr = check_nml_error (io, 'test_nml') #else nmlunit = open_namelist_file() ierr=1 diff --git a/src/shared/topography/gaussian_topog.F90 b/src/shared/topography/gaussian_topog.F90 index d76073a31..314563124 100644 --- a/src/shared/topography/gaussian_topog.F90 +++ b/src/shared/topography/gaussian_topog.F90 @@ -270,6 +270,7 @@ subroutine read_namelist #ifdef INTERNAL_FILE_NML read (input_nml_file, gaussian_topog_nml, iostat=io) + ierr = check_nml_error(io,'gaussian_topog_nml') #else if ( file_exist('input.nml')) then unit = open_namelist_file ( ) diff --git a/src/shared/topography/topography.F90 b/src/shared/topography/topography.F90 index cce81005b..156a5a50a 100644 --- a/src/shared/topography/topography.F90 +++ b/src/shared/topography/topography.F90 @@ -920,6 +920,7 @@ subroutine read_namelist #ifdef INTERNAL_FILE_NML read (input_nml_file, topography_nml, iostat=io) + ierr = check_nml_error(io,'topography_nml') #else if ( file_exist('input.nml')) then unit = open_namelist_file ( )