diff --git a/external/esmf b/external/esmf new file mode 160000 index 0000000000..3a9c142262 --- /dev/null +++ b/external/esmf @@ -0,0 +1 @@ +Subproject commit 3a9c142262b247189abd8dbca0d63e6dbb3a8207 diff --git a/external/pfunit b/external/pfunit new file mode 160000 index 0000000000..14339d668c --- /dev/null +++ b/external/pfunit @@ -0,0 +1 @@ +Subproject commit 14339d668c3f7440c408422dea68d750ee59ad9d diff --git a/lilac/.dockerignore b/lilac/.dockerignore new file mode 100644 index 0000000000..2fc5d54e03 --- /dev/null +++ b/lilac/.dockerignore @@ -0,0 +1,329 @@ +# Created by .ignore support plugin (hsz.mobi) + +### Vim template +# swap +[._]*.s[a-w][a-z] +[._]s[a-w][a-z] +# session +Session.vim +# temporary +.netrwhist +*~ + +# auto-generated tag files +tags + +### Cpp template +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +### CMake template +CMakeCache.txt +CMakeFiles +CMakeScripts +Makefile +cmake_install.cmake +install_manifest.txt +CTestTestfile.cmake + +### Emacs template +# -*- mode: gitignore; -*- +*~ +\#*\# +/.emacs.desktop +/.emacs.desktop.lock +*.elc +auto-save-list +tramp +.\#* + +# Org-mode +.org-id-locations +*_archive + +# flymake-mode +*_flymake.* + +# eshell files +/eshell/history +/eshell/lastdir + +# elpa packages +/elpa/ + +# reftex files +*.rel + +# AUCTeX auto folder +/auto/ + +# cask packages +.cask/ +dist/ + +# Flycheck +flycheck_*.el + +# server auth directory +/server/ + +# projectiles files +.projectile### VirtualEnv template +# Virtualenv +# http://iamzed.com/2009/05/07/a-primer-on-virtualenv/ +.Python +[Bb]in +[Ii]nclude +[Ll]ib +[Ll]ib64 +[Ll]ocal +[Ss]cripts +pyvenv.cfg +.venv +pip-selfcheck.json + +### Linux template +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +### C template +# Object files +*.o +*.ko +*.obj +*.elf + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su + +### Windows template +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk + +### KDevelop4 template +*.kdev4 +.kdev4/ + +### Python template +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# 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/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# IPython Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# dotenv +.env + +# virtualenv +venv/ +ENV/ + +# Spyder project settings +.spyderproject + +# Rope project settings +.ropeproject + +### Xcode template +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## Build generated +build/ +DerivedData/ + +## Various settings +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata/ + +## Other +*.moved-aside +*.xccheckout +*.xcscmblueprint + +### NodeJS template +# Logs +logs +*.log +npm-debug.log* + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directory +# https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git +node_modules + +build/ \ No newline at end of file diff --git a/lilac/.gitignore b/lilac/.gitignore index 411de5d96e..21537a4ee9 100644 --- a/lilac/.gitignore +++ b/lilac/.gitignore @@ -12,3 +12,5 @@ components/ # generated python files *.pyc + +build/ diff --git a/lilac/.gitmodules b/lilac/.gitmodules new file mode 100644 index 0000000000..3836c21e6b --- /dev/null +++ b/lilac/.gitmodules @@ -0,0 +1,8 @@ +[submodule "external/pfunit"] + path = external/pfunit + url = https://github.com/laristra/pfunit.git + shallow = true +[submodule "external/esmf"] + path = external/esmf + url = https://git.code.sf.net/p/esmf/esmf + shallow = true diff --git a/lilac/.travis.yml b/lilac/.travis.yml new file mode 100644 index 0000000000..40f2c1981a --- /dev/null +++ b/lilac/.travis.yml @@ -0,0 +1,21 @@ +language: cpp +sudo: required +dist: trusty +notifications: + email: false + +services: + - docker + +before_install: + - docker version + - docker-compose version + +install: + - docker-compose build lilac + +before_script: + - docker-compose images + +script: + - docker run -t lilac diff --git a/lilac/CMakeLists.txt b/lilac/CMakeLists.txt new file mode 100644 index 0000000000..4268ea956d --- /dev/null +++ b/lilac/CMakeLists.txt @@ -0,0 +1,38 @@ +cmake_minimum_required(VERSION 2.8.12.1) + +project(LILAC Fortran) +enable_language(Fortran) + +set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/CMakeModules") + +find_package(MPI REQUIRED) +# TODO: This should be found from the find_package call but its not working +set(CMAKE_Fortran_COMPILER "/usr/lib64/mpich/bin/mpif90") +find_package(ESMF REQUIRED) + +# Local CMake modules + +if(CMAKE_Fortran_COMPILER_ID MATCHES "GNU") + set(dialect "-ffree-form -std=f2008 -fimplicit-none") + set(bounds "-fbounds-check") +endif() +if(CMAKE_Fortran_COMPILER_ID MATCHES "Intel") + set(dialect "-stand f08 -free -implicitnone") + set(bounds "-check bounds") +endif() +if(CMAKE_Fortran_COMPILER_ID MATCHES "PGI") + set(dialect "-Mfreeform -Mdclchk -Mstandard -Mallocatable=03") + set(bounds "-C") +endif() + +set(CMAKE_Fortran_FLAGS_DEBUG "${CMAKE_Fortran_FLAGS_DEBUG} ${bounds}") +set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${dialect}") + +set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${ESMF_COMPILER_LINE}") +set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${ESMF_LINK_LINE} -g -cpp") +# TODO: This should not be necessary but certain header files are missing from the build +set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -I /usr/include/ -I/usr/src/lilac/external/esmf/src/Infrastructure/Util/include -I/usr/src/lilac/external/esmf/build_config/Linux.gfortran.default -I /usr/src/lilac/external/esmf/src/include") +message("CMAKE_Fortran_FLAGS:" ${CMAKE_Fortran_FLAGS}) + +add_subdirectory(lilac) +add_subdirectory(tests) diff --git a/lilac/Dockerfile b/lilac/Dockerfile new file mode 100644 index 0000000000..bebcc00428 --- /dev/null +++ b/lilac/Dockerfile @@ -0,0 +1,30 @@ +FROM centos:latest +LABEL description="LILAC development environment" + +RUN yum install -y curl +RUN yum upgrade -y +RUN yum update -y +RUN yum clean all +RUN yum -y install wget bzip2 gcc gcc-c++ gcc-gfortran mpich-devel make git +ENV PATH="/usr/lib64/mpich/bin:${PATH}" + +WORKDIR /usr/src/lilac/ + +RUN mkdir -p external +RUN mkdir -p ci + +COPY external/esmf external/esmf +COPY external/pfunit external/pfunit +COPY ci/* ci/ + +# Install some remaining dependencies +ENV PATH /usr/local/miniconda/bin:$PATH +RUN ./ci/install_python.sh + +# Install ESMF +RUN ./ci/install_esmf.sh +ENV ESMF_CONFIG_FILE /usr/local/lib/esmf.mk + +# Install PFUNIT +# RUN ./ci/install_pfunit.sh +# ENV PFUNIT_INSTALL /usr/pfunit diff --git a/lilac/README.md b/lilac/README.md index a7a62fb359..4184eb80d3 100644 --- a/lilac/README.md +++ b/lilac/README.md @@ -2,3 +2,11 @@ LILAC, Lightweight Infrastructure for Land Atmosphere Coupling. +[![Build Status](https://travis-ci.org/jhamman/lilac.svg?branch=master)](https://travis-ci.org/jhamman/lilac) +[![Documentation Status](https://readthedocs.org/projects/ctsm-lilac/badge/?version=latest)](https://ctsm-lilac.readthedocs.io/en/latest/?badge=latest) + +Currently working on: + - Setting up CI and CMake + - setup/test style (borrow from geostreams) + - setup unit tests with pfunit + diff --git a/lilac/ci/build_and_test_lilac.sh b/lilac/ci/build_and_test_lilac.sh new file mode 100755 index 0000000000..3b7fca20f5 --- /dev/null +++ b/lilac/ci/build_and_test_lilac.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +set -e +set -x + +echo "building lilac" + +# build lilac +mkdir -p /lilac/build + +export CMAKE_PREFIX_PATH=/usr/lib64/mpich/bin + +cd /lilac/build && cmake -D CMAKE_BUILD_TYPE=DEBUG .. +make VERBOSE=1 # -j 4 + +echo "done building lilac, time to run the tests..." + +# run test suite +ctest \ No newline at end of file diff --git a/lilac/ci/code_format.sh b/lilac/ci/code_format.sh new file mode 100755 index 0000000000..828b0e229a --- /dev/null +++ b/lilac/ci/code_format.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +# Run emacs/list on all Fortran source files +format_fortran () { + echo "Parsing $1 as language Fortran" + emacs --batch -l ./emacs-fortran-formating-script.lisp \ + -f f90-batch-indent-region $1 +} +export -f format_fortran +find ../lilac/ -iregex ".*\.F[0-9]*" -exec bash -c 'format_fortran "$0"' {} \; +find ../tests/ -iregex ".*\.F[0-9]*" -exec bash -c 'format_fortran "$0"' {} \; diff --git a/lilac/ci/emacs-fortran-formating-script.lisp b/lilac/ci/emacs-fortran-formating-script.lisp new file mode 100644 index 0000000000..99ed3f3981 --- /dev/null +++ b/lilac/ci/emacs-fortran-formating-script.lisp @@ -0,0 +1,46 @@ +(defun f90-batch-indent-region () + "Run `f90-batch-beatify-region' on the specified filename. +Use this from the command line, with `-batch'; +it won't work in an interactive Emacs. +For example, invoke \"emacs -batch -l ~/.emacs-batch-f90-indent -f f90-batch-indent-region file.f\"" + (if (not noninteractive) + (error "`f90-batch-indent-region' is to be used only with -batch")) + (let ((make-backup-files nil) + (version-control nil) + (auto-save-default nil) + (find-file-run-dired nil) + (kept-old-versions 259259) + (kept-new-versions 259259)) + (let ((error 0) + file + (files ())) + (while command-line-args-left + (setq file (expand-file-name (car command-line-args-left))) + (cond ((not (file-exists-p file)) + (message ">> %s does not exist!" file) + (setq error 1 + command-line-args-left (cdr command-line-args-left))) + ((file-directory-p file) + (setq command-line-args-left + (nconc (directory-files file) + (cdr command-line-args-left)))) + (t + (setq files (cons file files) + command-line-args-left (cdr command-line-args-left))))) + (while files + (setq file (car files) + files (cdr files)) + (condition-case err + (progn + (if buffer-file-name (kill-buffer (current-buffer))) + (find-file file) + (buffer-disable-undo (current-buffer)) + (set-buffer-modified-p nil) + (f90-mode) + (message (file-name-nondirectory buffer-file-name)) + ; compute indentation of first + ; line + (f90-indent-subprogram) + (f90-downcase-keywords) + (save-buffer) +)))))) diff --git a/lilac/ci/environment.yml b/lilac/ci/environment.yml new file mode 100644 index 0000000000..735c333293 --- /dev/null +++ b/lilac/ci/environment.yml @@ -0,0 +1,5 @@ +channels: + - conda-forge +dependencies: + - python=3.6 + - cmake>=3 diff --git a/lilac/ci/install_esmf.sh b/lilac/ci/install_esmf.sh new file mode 100755 index 0000000000..bbd21faddb --- /dev/null +++ b/lilac/ci/install_esmf.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +set -e +set -x + +cd ./external/esmf + +export FC="mpif90" + +export ESMF_DIR=$PWD +export ESMF_COMM="mpich3" +export ESMF_COMPILER="gfortran" +export ESMF_INSTALL_PREFIX="/usr/local" +export ESMF_INSTALL_LIBDIR="/usr/local/lib" +export ESMF_INSTALL_MODDIR="/usr/local/mod" +export ESMF_INSTALL_BINDIR="/usr/local/bin" +export ESMF_INSTALL_DOCDIR="/usr/local/doc" +export ESMFMKFILE="${ESMF_INSTALL_LIBDIR}/esmf.mk" + +make -j4 lib +make install +# make install check + +cd - \ No newline at end of file diff --git a/lilac/ci/install_pfunit.sh b/lilac/ci/install_pfunit.sh new file mode 100755 index 0000000000..9290ab7306 --- /dev/null +++ b/lilac/ci/install_pfunit.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +set -e +set -x + +cd ./external/pfunit + +# set environemnt variables +export F90=gfortran +export F90_VENDOR=GNU + +mkdir -p build +cd build +cmake .. +make install INSTALL_DIR=/usr/pfunit + +cd - diff --git a/lilac/ci/install_python.sh b/lilac/ci/install_python.sh new file mode 100755 index 0000000000..02a15e043c --- /dev/null +++ b/lilac/ci/install_python.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +set -e +set -x + +# Install miniconda +wget --quiet http://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O /usr/src/miniconda.sh +bash /usr/src/miniconda.sh -b -p /usr/local/miniconda +conda update conda --yes +conda clean -tipy +conda config --set always_yes yes --set changeps1 no +conda --version + +conda install -c conda-forge cmake>=3 diff --git a/lilac/cmake/CMakeModules/FindESMF.cmake b/lilac/cmake/CMakeModules/FindESMF.cmake new file mode 100644 index 0000000000..943ffd6087 --- /dev/null +++ b/lilac/cmake/CMakeModules/FindESMF.cmake @@ -0,0 +1,52 @@ +# +# Author: Ali Samii - The University of Texas at Austin +# +# Distributed under GPL2. For more info refer to: +# https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html +# +# +# FindESMF +# -------- +# +# This script tries to find the ESMF library. You have to define +# the path to esmf.mk file in your installation directory. +# +# There are plans to extend this script to find ESMF automatically, +# but until then, you should set the environment variable +# +# ESMF_CONFIG_FILE = /path/to/esmf.mk +# +# in your installation directory. The output will be +# +# ESMF_LINK_LINE : All the libraries and link line stuff +# ESMF_COMPILER_LINE : All the compiler flags and include dirs +# +# + +# Defining the ${Esc} for syntax coloring. +string(ASCII 27 Esc) + +message ("Parsing ESMF_CONFIG_FILE: " $ENV{ESMF_CONFIG_FILE}) + +file(STRINGS "$ENV{ESMF_CONFIG_FILE}" all_vars) +foreach(str ${all_vars}) + string(REGEX MATCH "^[^#]" def ${str}) + if (def) + string(REGEX MATCH "^[^=]+" var_name ${str}) + string(REGEX MATCH "=(.+)$" var_def ${str}) + set(var_def ${CMAKE_MATCH_1}) + set(${var_name} ${var_def}) + mark_as_advanced (${var_name}) + endif() +endforeach() + +set (ESMF_LINK_LINE "${ESMF_F90LINKOPTS} \ + ${ESMF_F90LINKRPATHS} \ + ${ESMF_F90LINKPATHS} \ + ${ESMF_F90ESMFLINKLIBS}") + +set (ESMF_COMPILER_LINE "${ESMF_F90COMPILEOPTS} \ + ${ESMF_F90COMPILEPATHS} \ + ${ESMF_F90COMPILEFREENOCPP} \ + ${ESMF_CXXCOMPILEPATHS}") + diff --git a/lilac/docker-compose.yml b/lilac/docker-compose.yml new file mode 100644 index 0000000000..422b0bc607 --- /dev/null +++ b/lilac/docker-compose.yml @@ -0,0 +1,10 @@ +version: '3' + +services: + lilac: + build: . + container_name: lilac + volumes: + - .:/lilac + command: /lilac/ci/build_and_test_lilac.sh + diff --git a/lilac/docs/Makefile b/lilac/docs/Makefile new file mode 100644 index 0000000000..0e382f9883 --- /dev/null +++ b/lilac/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +SPHINXPROJ = lilac +SOURCEDIR = . +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) \ No newline at end of file diff --git a/lilac/docs/api.rst b/lilac/docs/api.rst new file mode 100644 index 0000000000..dac6446ce8 --- /dev/null +++ b/lilac/docs/api.rst @@ -0,0 +1,20 @@ +LILAC API +========= + +LILAC provides a high-level API (in Fortran) for coupling to CTSM. +LILAC was built using the assumption that the Atmosphere model +component in each LILAC application would opperate as the "driver". + +The atmosphere component will need to call each of the following subroutines: + + * `lilac_init` + * `lilac_run` + * `lilac_final` + +LILAC Core +---------- +.. f:autosrcfile:: core.f90 + +ESMF Utils +---------- +.. f:autosrcfile:: esmf_utils.f90 diff --git a/lilac/docs/conf.py b/lilac/docs/conf.py new file mode 100644 index 0000000000..f8c67d5d2b --- /dev/null +++ b/lilac/docs/conf.py @@ -0,0 +1,187 @@ +# -*- coding: utf-8 -*- +# +# 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/stable/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('.')) + + +# -- Project information ----------------------------------------------------- + +project = 'lilac' +copyright = '2018, Joseph Hamman' +author = 'Joseph Hamman' + +# The short X.Y version +version = '' +# The full version, including alpha/beta/rc tags +release = '' + + +# -- 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.autodoc', + 'sphinx.ext.todo', + 'sphinx.ext.coverage', + 'sphinx.ext.imgmath', + 'sphinx.ext.ifconfig', + 'sphinx.ext.intersphinx', + 'sphinxfortran.fortran_domain', + 'sphinxfortran.fortran_autodoc', +] + +# 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' +fortran_src = '*f90' + +# 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 = ['_build', 'Thumbs.db', '.DS_Store'] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + + +# -- 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 = 'alabaster' + +# 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 = {} + + +# -- Options for HTMLHelp output --------------------------------------------- + +# Output file base name for HTML help builder. +htmlhelp_basename = 'lilacdoc' + + +# -- 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, 'lilac.tex', 'lilac Documentation', + 'Joseph Hamman', '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, 'lilac', 'lilac 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, 'lilac', 'lilac Documentation', + author, 'lilac', 'One line description of project.', + 'Miscellaneous'), +] + + +# -- Extension configuration ------------------------------------------------- + +# -- Options for intersphinx extension --------------------------------------- + +# Example configuration for intersphinx: refer to the Python standard library. +intersphinx_mapping = {'https://docs.python.org/': None} + + +## -- Options for Sphinx-Fortran --------------------------------------------- +# List of possible extensions in the case of a directory listing +fortran_ext = ['f90', 'F90', 'f95', 'F95'] + +# This variable must be set with file pattern, like "*.f90", or a list of them. +# It is also possible to specify a directory name; in this case, all files than +# have an extension matching those define by the config variable `fortran_ext` +# are used. +fortran_src = [os.path.abspath('../lilac/')] + +# Indentation string or length (default 4). If it is an integer, +# indicates the number of spaces. +fortran_indent = 4 diff --git a/lilac/docs/developers.rst b/lilac/docs/developers.rst new file mode 100644 index 0000000000..f4910d80d6 --- /dev/null +++ b/lilac/docs/developers.rst @@ -0,0 +1,28 @@ +Developers Guide to Using LILAC +=============================== + +Building LILAC +-------------- + +LILAC can be build using CMake:: + + $ cd /lilac/build && cmake .. + $ make + +For development and testing purposes, LILAC can also be built using a +`docker-compose` script:: + + $ docker-compose build + $ docker-compose run + +Testing LILAC +------------- + +LILAC includes a full test suite including unit tests and a number of +simplified coupled model tests. To run these tests, simply run the `ctest` +command:: + + $ ctest + +Note, if you are using the docker-compose development, the `docker-compose run` +command will build and run LILAC automatically. diff --git a/lilac/docs/index.rst b/lilac/docs/index.rst new file mode 100644 index 0000000000..e7c7a097f7 --- /dev/null +++ b/lilac/docs/index.rst @@ -0,0 +1,16 @@ +LILAC: Lightweight Infrastructure for Land Atmosphere Coupling +============================================================== + +LILAC is a new coupling interface for the Community Terrestrial Systems Model +(CTSM). It provides a high-level Fortran API for coupling CTSM to atmospheric +models such as the Weather Research and Forecast (WRF) model. LILAC makes +extensive use of the Earth System Modeling Framework (ESMF). + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + +Contents +--------------- +* :doc:`developers` +* :doc:`api` diff --git a/lilac/docs/requirements.txt b/lilac/docs/requirements.txt new file mode 100644 index 0000000000..f3d707325c --- /dev/null +++ b/lilac/docs/requirements.txt @@ -0,0 +1,3 @@ +numpy +sphinx-fortran +sphinx==1.6.7 \ No newline at end of file diff --git a/lilac/lilac/CMakeLists.txt b/lilac/lilac/CMakeLists.txt new file mode 100644 index 0000000000..a92669e0ff --- /dev/null +++ b/lilac/lilac/CMakeLists.txt @@ -0,0 +1,4 @@ +# Compile LILAC library +file(GLOB_RECURSE LILAC_SOURCES *.f90 *.h) +add_library(lilac ${LILAC_SOURCES}) +target_include_directories(lilac PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/lilac/lilac/core.f90 b/lilac/lilac/core.f90 new file mode 100644 index 0000000000..91ff33fd6c --- /dev/null +++ b/lilac/lilac/core.f90 @@ -0,0 +1,317 @@ +module lilac + + use ESMF + use esmf_utils + + implicit none + + character(*), parameter :: modname = "(core)" + + !-------------------------------------------------------------------------- + ! Public interfaces + !-------------------------------------------------------------------------- + public :: init + public :: run + public :: final + + private :: atmos_register + private :: land_register + private :: cpl_register + + type, public :: LilacType + private + + type(ESMFInfoType) :: esmf_info + character(len=ESMF_MAXSTR) :: name + + contains + procedure, public :: init => init + procedure, public :: run => run + procedure, public :: final => final + + ! register methods + procedure, nopass, private :: atmos_register => atmos_register + procedure, nopass, private :: land_register => land_register + procedure, nopass, private :: cpl_register => cpl_register + + ! Init methods + procedure, nopass, private :: atmos_init => atmos_init + procedure, nopass, private :: land_init => land_init + procedure, nopass, private :: coupler_init => coupler_init + + ! Run methods + procedure, nopass, private :: atmos_copy_atm_to_lilac => atmos_copy_atm_to_lilac + procedure, nopass, private :: atmos_copy_lilac_to_atm => atmos_copy_lilac_to_atm + procedure, nopass, private :: land_run => land_run + procedure, nopass, private :: coupler_run => coupler_run + + ! Final methods + procedure, nopass, private :: atmos_final => atmos_final + procedure, nopass, private :: land_final => land_final + procedure, nopass, private :: coupler_final => coupler_final + + end type LilacType + +contains + + subroutine init(self, name) + implicit none + class(LilacType), intent(inout) :: self + character(len=ESMF_MAXSTR), intent(in) :: name + + character(len=*), parameter :: subname=trim(modname)//':(init) ' + + call ESMF_LogWrite(subname//"Initializing lilac", ESMF_LOGMSG_INFO) + + self%name = trim(name) + + ! Initialize ESMF structures + call self%esmf_info%init(name, atmos_register, land_register, cpl_register) + + end subroutine init + + subroutine run(self) + implicit none + class(LilacType), intent(inout) :: self + + character(len=*), parameter :: subname=trim(modname)//':(run) ' + + call ESMF_LogWrite(subname//"Running lilac", ESMF_LOGMSG_INFO) + + call self%esmf_info%run() + + end subroutine run + + subroutine final(self) + implicit none + class(LilacType), intent(inout) :: self + + character(len=*), parameter :: subname=trim(modname)//':(final) ' + + call ESMF_LogWrite(subname//"Finalizing lilac", ESMF_LOGMSG_INFO) + + call self%esmf_info%final() + + end subroutine final + + subroutine atmos_register(comp, rc) + type(ESMF_GridComp) :: comp ! must not be optional + integer, intent(out) :: rc ! must not be optional + character(len=*), parameter :: subname=trim(modname)//':(atmos_register) ' + + ! Set the entry points for standard ESMF Component methods + call ESMF_GridCompSetEntryPoint(comp, ESMF_METHOD_INITIALIZE, & + userRoutine=atmos_init, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) return ! bail out + + call ESMF_GridCompSetEntryPoint(comp, ESMF_METHOD_RUN, & + userRoutine=atmos_copy_atm_to_lilac, phase=1, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) return ! bail out + + call ESMF_GridCompSetEntryPoint(comp, ESMF_METHOD_RUN, & + userRoutine=atmos_copy_lilac_to_atm, phase=2, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) return ! bail out + + call ESMF_GridCompSetEntryPoint(comp, ESMF_METHOD_FINALIZE, & + userRoutine=atmos_final, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) return ! bail out + + rc = ESMF_SUCCESS + + end subroutine atmos_register + + subroutine land_register(comp, rc) + type(ESMF_GridComp) :: comp ! must not be optional + integer, intent(out) :: rc ! must not be optional + character(len=*), parameter :: subname=trim(modname)//':(lnd_register) ' + + ! land_* comes from ctsm esmf cap + + ! Set the entry points for standard ESMF Component methods + call ESMF_GridCompSetEntryPoint(comp, ESMF_METHOD_INITIALIZE, userRoutine=land_init, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) return ! bail out + + call ESMF_GridCompSetEntryPoint(comp, ESMF_METHOD_RUN, userRoutine=land_run, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) return ! bail out + + call ESMF_GridCompSetEntryPoint(comp, ESMF_METHOD_FINALIZE, userRoutine=land_final, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) return ! bail out + + rc = ESMF_SUCCESS + + end subroutine land_register + + subroutine cpl_register(comp, rc) + type(ESMF_CplComp) :: comp ! must not be optional + integer, intent(out) :: rc ! must not be optional + character(len=*), parameter :: subname=trim(modname)//':(cpl_register) ' + + rc = ESMF_FAILURE + + ! Register the callback routines. + call ESMF_CplCompSetEntryPoint(comp, ESMF_METHOD_INITIALIZE, userRoutine=coupler_init, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) return ! bail out + + call ESMF_CplCompSetEntryPoint(comp, ESMF_METHOD_RUN, userRoutine=coupler_run, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) return ! bail out + + call ESMF_CplCompSetEntryPoint(comp, ESMF_METHOD_FINALIZE, userRoutine=coupler_final, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) return ! bail out + + call ESMF_LogWrite(subname//"CouplerMod: Registered Initialize, Run, and Finalize routines", ESMF_LOGMSG_INFO) + + rc = ESMF_SUCCESS + + end subroutine cpl_register + + subroutine atmos_init(comp, importState, exportState, clock, rc) + type(ESMF_GridComp) :: comp + type(ESMF_State) :: importState, exportState + type(ESMF_Clock) :: clock + integer, intent(out) :: rc + + character(len=*), parameter :: subname=trim(modname)//':(atmos_init) ' + + ! Initialize return code + rc = ESMF_SUCCESS + + call ESMF_LogWrite(subname//"atmos_init has not been implemented yet", ESMF_LOGMSG_INFO) + + end subroutine atmos_init + + subroutine land_init(comp, importState, exportState, clock, rc) + type(ESMF_GridComp) :: comp + type(ESMF_State) :: importState, exportState + type(ESMF_Clock) :: clock + integer, intent(out) :: rc + + character(len=*), parameter :: subname=trim(modname)//':(land_init) ' + + ! Initialize return code + rc = ESMF_SUCCESS + + call ESMF_LogWrite(subname//"land_init has not been implemented yet", ESMF_LOGMSG_INFO) + + end subroutine land_init + + subroutine coupler_init(comp, importState, exportState, clock, rc) + type(ESMF_CplComp) :: comp + type(ESMF_State) :: importState, exportState + type(ESMF_Clock) :: clock + integer, intent(out) :: rc + + character(len=*), parameter :: subname=trim(modname)//':(coupler_init) ' + + ! Initialize return code + rc = ESMF_SUCCESS + + call ESMF_LogWrite(subname//"coupler_init has not been implemented yet", ESMF_LOGMSG_INFO) + + end subroutine coupler_init + + subroutine atmos_copy_atm_to_lilac(comp, importState, exportState, clock, rc) + type(ESMF_GridComp) :: comp + type(ESMF_State) :: importState, exportState + type(ESMF_Clock) :: clock + integer, intent(out) :: rc + + character(len=*), parameter :: subname=trim(modname)//':(atmos_copy_atm_to_lilac) ' + + ! Initialize return code + rc = ESMF_SUCCESS + + call ESMF_LogWrite(subname//"atmos_copy_atm_to_lilac has not been implemented yet", ESMF_LOGMSG_INFO) + + end subroutine atmos_copy_atm_to_lilac + + subroutine atmos_copy_lilac_to_atm(comp, importState, exportState, clock, rc) + type(ESMF_GridComp) :: comp + type(ESMF_State) :: importState, exportState + type(ESMF_Clock) :: clock + integer, intent(out) :: rc + + character(len=*), parameter :: subname=trim(modname)//':(atmos_copy_lilac_to_atm) ' + + ! Initialize return code + rc = ESMF_SUCCESS + + call ESMF_LogWrite(subname//"atmos_copy_lilac_to_atm has not been implemented yet", ESMF_LOGMSG_INFO) + + end subroutine atmos_copy_lilac_to_atm + + subroutine land_run(comp, importState, exportState, clock, rc) + type(ESMF_GridComp) :: comp + type(ESMF_State) :: importState, exportState + type(ESMF_Clock) :: clock + integer, intent(out) :: rc + + character(len=*), parameter :: subname=trim(modname)//':(land_run) ' + + ! Initialize return code + rc = ESMF_SUCCESS + + call ESMF_LogWrite(subname//"land_run has not been implemented yet", ESMF_LOGMSG_INFO) + + end subroutine land_run + + subroutine coupler_run(comp, importState, exportState, clock, rc) + type(ESMF_CplComp) :: comp + type(ESMF_State) :: importState, exportState + type(ESMF_Clock) :: clock + integer, intent(out) :: rc + + character(len=*), parameter :: subname=trim(modname)//':(coupler_run) ' + + ! Initialize return code + rc = ESMF_SUCCESS + + call ESMF_LogWrite(subname//"coupler_run has not been implemented yet", ESMF_LOGMSG_INFO) + + end subroutine coupler_run + + subroutine atmos_final(comp, importState, exportState, clock, rc) + type(ESMF_GridComp) :: comp + type(ESMF_State) :: importState, exportState + type(ESMF_Clock) :: clock + integer, intent(out) :: rc + + character(len=*), parameter :: subname=trim(modname)//':(atmos_final) ' + + ! Initialize return code + rc = ESMF_SUCCESS + + call ESMF_LogWrite(subname//"atmos_final has not been implemented yet", ESMF_LOGMSG_INFO) + + end subroutine atmos_final + + subroutine land_final(comp, importState, exportState, clock, rc) + type(ESMF_GridComp) :: comp + type(ESMF_State) :: importState, exportState + type(ESMF_Clock) :: clock + integer, intent(out) :: rc + + character(len=*), parameter :: subname=trim(modname)//':(land_final) ' + + ! Initialize return code + rc = ESMF_SUCCESS + + call ESMF_LogWrite(subname//"land_final has not been implemented yet", ESMF_LOGMSG_INFO) + + end subroutine land_final + + subroutine coupler_final(comp, importState, exportState, clock, rc) + type(ESMF_CplComp) :: comp + type(ESMF_State) :: importState, exportState + type(ESMF_Clock) :: clock + integer, intent(out) :: rc + + character(len=*), parameter :: subname=trim(modname)//':(coupler_final) ' + + ! Initialize return code + rc = ESMF_SUCCESS + + call ESMF_LogWrite(subname//"coupler_final has not been implemented yet", ESMF_LOGMSG_INFO) + + end subroutine coupler_final + +end module lilac diff --git a/lilac/lilac/esmf_utils.f90 b/lilac/lilac/esmf_utils.f90 new file mode 100644 index 0000000000..57f9cf0168 --- /dev/null +++ b/lilac/lilac/esmf_utils.f90 @@ -0,0 +1,221 @@ +module esmf_utils + + ! Wrappers and derived types exposing ESMF components to LILAC + + +#include "ESMF.h" + use ESMF + + implicit none + private + + character(*), parameter :: modname = "(esmf_utils)" + + interface + subroutine userRoutine(gridcomp, rc) + use ESMF_CompMod + implicit none + type(ESMF_GridComp) :: gridcomp ! must not be optional + integer, intent(out) :: rc ! must not be optional + end subroutine userRoutine + end interface + + interface + subroutine userCplRoutine(cplcomp, rc) + use ESMF_CompMod + implicit none + type(ESMF_CplComp) :: cplcomp ! must not be optional + integer, intent(out) :: rc ! must not be optional + end subroutine userCplRoutine + end interface + + ! Consider renaming ESMFInfoType (add lilac to name) + type, public :: ESMFInfoType + private + character(len=ESMF_MAXSTR) :: name + + type(ESMF_VM) :: vm + type(ESMF_State) :: land_import + type(ESMF_State) :: land_export + type(ESMF_State) :: atmos_import + type(ESMF_State) :: atmos_export + type(ESMF_GridComp) :: atmos_comp + type(ESMF_GridComp) :: land_comp + type(ESMF_CplComp) :: cpl_comp + + contains + procedure, public :: init => init + procedure, public :: run => run + procedure, public :: final => final + + end type ESMFInfoType + +contains + + subroutine init(self, name, atmos_register, land_register, cpl_register) + implicit none + class(ESMFInfoType), intent(inout) :: self + character(len=ESMF_MAXSTR), intent(in) :: name + procedure(userRoutine) :: atmos_register + procedure(userRoutine) :: land_register + procedure(userCplRoutine) :: cpl_register + + ! Local variables + character(len=ESMF_MAXSTR) :: cname1, cname2, cplname + integer :: localPet, petCount, rc=ESMF_SUCCESS + + character(len=*), parameter :: subname=trim(modname)//':(init) ' + + call ESMF_LogWrite(subname//"esmf_info%init()", ESMF_LOGMSG_INFO) + + self%name = trim(name) + + ! Create section + !------------------------------------------------------------------------- + + ! Initialize framework and get back default global VM + + ! only run if not esmf_isintialized() + call ESMF_Initialize(vm=self%vm, defaultlogfilename="lilac.log", logkindflag=ESMF_LOGKIND_MULTI, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) return ! bail out + + ! Get number of PETs we are running with + call ESMF_VMGet(self%vm, petCount=petCount, localPet=localPet, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) return ! bail out + + ! Create the 2 model components and a coupler + cname1 = "land" + ! use petList to define land on all PET + self%land_comp = ESMF_GridCompCreate(name=cname1, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) return ! bail out + call ESMF_LogWrite(subname//"Created "//trim(cname1)//" component", ESMF_LOGMSG_INFO) + + cname2 = "atmosphere" + ! use petList to define atmosphere on all PET + self%atmos_comp = ESMF_GridCompCreate(name=cname2, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) return ! bail out + call ESMF_LogWrite(subname//"Created "//trim(cname2)//" component", ESMF_LOGMSG_INFO) + + cplname = "lilac coupler" + ! no petList means that coupler component runs on all PETs + self%cpl_comp = ESMF_CplCompCreate(name=cplname, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) return ! bail out + call ESMF_LogWrite(subname//"Created "//trim(cplname)//" component", ESMF_LOGMSG_INFO) + + call ESMF_LogWrite(subname//"Comp Creates finished", ESMF_LOGMSG_INFO) + + ! Register section + !------------------------------------------------------------------------- + call ESMF_GridCompSetServices(self%atmos_comp, userRoutine=atmos_register, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) return ! bail out + call ESMF_LogWrite(subname//"atmos SetServices finished", ESMF_LOGMSG_INFO) + + call ESMF_GridCompSetServices(self%land_comp, userRoutine=land_register, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) return ! bail out + call ESMF_LogWrite(subname//"land SetServices finished", ESMF_LOGMSG_INFO) + + call ESMF_CplCompSetServices(self%cpl_comp, userRoutine=cpl_register, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) return ! bail out + call ESMF_LogWrite(subname//"Cpl SetServices finished", ESMF_LOGMSG_INFO) + + ! Init section + !------------------------------------------------------------------------- + ! land import/export states + self%land_import = ESMF_StateCreate(name="land import", stateintent=ESMF_STATEINTENT_IMPORT, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) return ! bail out + + self%land_export = ESMF_StateCreate(name="land export", stateintent=ESMF_STATEINTENT_EXPORT, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) return ! bail out + + call ESMF_GridCompInitialize(self%land_comp, importState=self%land_import, exportState=self%land_export, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) return ! bail out + call ESMF_LogWrite(subname//"Land Initialize finished", ESMF_LOGMSG_INFO) + + ! atmosphere import/export state + self%atmos_import = ESMF_StateCreate(name="atmos import", stateintent=ESMF_STATEINTENT_IMPORT, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) return ! bail out + + self%atmos_export = ESMF_StateCreate(name="atmos export", stateintent=ESMF_STATEINTENT_EXPORT, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) return ! bail out + + call ESMF_GridCompInitialize(self%atmos_comp, exportState=self%atmos_export, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) return ! bail out + call ESMF_LogWrite(subname//"Atmosphere Initialize finished", ESMF_LOGMSG_INFO) + + ! call ESMF_CPLCompInitialize twice (once for each grid comp) + + end subroutine init + + subroutine run(self) + implicit none + class(ESMFInfoType), intent(inout) :: self + integer :: rc=ESMF_SUCCESS + character(len=*), parameter :: subname=trim(modname)//':(run) ' + + call ESMF_LogWrite(subname//"esmf_info%run()", ESMF_LOGMSG_INFO) + + ! TODO: need some help on order of imports/exports/runs and whether the land/atm both need import/export states + + ! atmosphere run + ! copy the atmos state and put it into atmos export + call ESMF_GridCompRun(self%atmos_comp, exportState=self%atmos_export, phase=1, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) return ! bail out + call ESMF_LogWrite(subname//"Atmosphere Run returned", ESMF_LOGMSG_INFO) + + ! coupler run + call ESMF_CplCompRun(self%cpl_comp, importState=self%atmos_export, exportState=self%land_import, & + rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) return ! bail out + call ESMF_LogWrite(subname//"Coupler Run returned", ESMF_LOGMSG_INFO) + + ! land run + call ESMF_GridCompRun(self%land_comp, importState=self%land_import, exportState=self%land_export, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) return ! bail out + call ESMF_LogWrite(subname//"Land Run returned", ESMF_LOGMSG_INFO) + + ! coupler run + call ESMF_CplCompRun(self%cpl_comp, importState=self%land_export, exportState=self%atmos_import, rc=rc) + call ESMF_LogWrite(subname//"Coupler Run returned", ESMF_LOGMSG_INFO) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) return ! bail out + + call ESMF_GridCompRun(self%atmos_comp, importState=self%atmos_import, phase=2, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) return ! bail out + call ESMF_LogWrite(subname//"Atmosphere Run returned", ESMF_LOGMSG_INFO) + + end subroutine run + + subroutine final(self) + implicit none + class(ESMFInfoType), intent(inout) :: self + integer :: rc=ESMF_SUCCESS + character(len=*), parameter :: subname=trim(modname)//':(final) ' + + call ESMF_LogWrite(subname//"esmf_info%final()", ESMF_LOGMSG_INFO) + + ! Destroy section + call ESMF_GridCompDestroy(self%atmos_comp, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) return ! bail out + + call ESMF_GridCompDestroy(self%land_comp, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) return ! bail out + + call ESMF_CplCompDestroy(self%cpl_comp, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) return ! bail out + + call ESMF_StateDestroy(self%land_export, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) return ! bail out + + call ESMF_StateDestroy(self%land_import, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) return ! bail out + + call ESMF_StateDestroy(self%atmos_export, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) return ! bail out + + call ESMF_StateDestroy(self%atmos_import, rc=rc) + if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=__FILE__)) return ! bail out + + call ESMF_LogWrite(subname//"All Destroy routines done", ESMF_LOGMSG_INFO) + + end subroutine final + +end module esmf_utils diff --git a/lilac/src/.dir-locals.el b/lilac/src/.dir-locals.el deleted file mode 100644 index 4edf085c03..0000000000 --- a/lilac/src/.dir-locals.el +++ /dev/null @@ -1,14 +0,0 @@ -;;; Directory Local Variables -;;; For more information see (info "(emacs) Directory Variables") - -((f90-mode - (f90-program-indent . 3) - (f90-associate-indent . 3) - (f90-do-indent . 3) - (f90-if-indent . 3) - (f90-type-indent . 3) - (f90-program-indent . 3) - (f90-continuation-indent . 5) - (fill-column . 80) - (indent-tabs-mode))) - diff --git a/lilac/src/lilac-demo-driver.F90 b/lilac/src/lilac-demo-driver.F90 deleted file mode 100644 index 328b869cf8..0000000000 --- a/lilac/src/lilac-demo-driver.F90 +++ /dev/null @@ -1,283 +0,0 @@ -program lilac_demo_driver - - use lnd_comp_mct , only: lnd_init_mct, lnd_run_mct, lnd_final_mct - use seq_flds_mod , only: & - seq_flds_x2l_states, seq_flds_x2l_fluxes, seq_flds_x2l_fields, & - seq_flds_l2x_states, seq_flds_l2x_fluxes, seq_flds_l2x_fields, & - seq_flds_dom_coord, seq_flds_dom_other, seq_flds_dom_fields - use seq_cdata_mod, only: seq_cdata - use seq_infodata_mod, only: seq_infodata_type, seq_infodata_putdata, seq_infodata_getdata - use shr_sys_mod , only: shr_sys_flush, shr_sys_abort - use shr_orb_mod , only: shr_orb_params - use shr_pio_mod , only: shr_pio_init1, shr_pio_init2 - use mct_mod - use ESMF - - implicit none - -#include ! mpi library include file - - !----- Clocks ----- - type(ESMF_Clock) :: driver_clock - type(ESMF_Time) :: CurrTime, StartTime, StopTime - type(ESMF_TimeInterval) :: TimeStep - type(ESMF_Alarm) :: EAlarm_stop, EAlarm_rest - type(ESMF_Calendar), target :: Calendar - integer :: yy, mm, dd, sec - - !----- MPI/MCT ----- - integer :: mpicom_lilac ! local mpicom - integer :: ID_lilac ! mct ID - integer :: ncomps ! number of separate components for MCT - integer :: ntasks, mytask ! mpicom size and rank - integer :: global_comm ! copy of mpi_comm_world for pio - integer, allocatable :: comp_id(:) ! for pio init2 - logical, allocatable :: comp_iamin(:) ! for pio init2 - character(len=64), allocatable :: comp_name(:) ! for pio init2 - integer, allocatable :: comp_comm(:), comp_comm_iam(:) ! for pio_init2 - - !----- Land Coupling Data ----- - type(seq_cdata) :: cdata ! Input land-model driver data - type(seq_infodata_type), target :: infodata ! infodata type - type(mct_aVect) :: x2l, l2x ! land model import and export states - type(mct_gGrid), target :: dom_lnd ! domain data for clm - type(mct_gsMap), target :: gsmap_lnd ! gsmap data for clm - integer :: orb_iyear ! Orbital - real*8 :: orb_eccen, orb_obliq, orb_mvelp, orb_obliqr, orb_lambm0, orb_mvelpp - character(len=128) :: case_name, case_desc, model_version, hostname, username - character(len=128) :: start_type - logical :: brnch_retain_casename, single_column, atm_aero - real*8 :: scmlat, scmlon - integer :: idx_Sa_z, idx_Sa_u, idx_Sa_v, idx_Sa_tbot, idx_Sa_ptem, & - idx_Sa_shum, idx_Sa_pbot, idx_Faxa_rainc, idx_Faxa_rainl, & - idx_Faxa_snowc, idx_Faxa_snowl, idx_Faxa_lwdn, idx_Faxa_swndr, & - idx_Faxa_swvdr, idx_Faxa_swndf, idx_Faxa_swvdf - - !----- Atm Model ----- - integer :: atm_nx, atm_ny - integer :: gsize, lsize, gstart, gend ! domain decomp info - integer, allocatable :: gindex(:) ! domain decomp info - type(mct_aVect) :: x2l_a ! data for land on atm decomp - type(mct_aVect) :: l2x_a ! data from land on atm decomp - type(mct_gsMap) :: gsmap_atm ! gsmap data for atm - type(mct_rearr) :: rearr_atm2lnd ! rearranger for atm to land - type(mct_rearr) :: rearr_lnd2atm ! rearranger for land to atm - - !----- Other ----- - integer :: n, m ! counter - character(len=128) :: string ! temporary string - integer :: ierr, rc ! local error status - integer :: iunit = 250 ! lilac log unit number - integer :: sunit = 249 ! share log unit number - character(len=*), parameter :: subname = 'lilac_demo_driver' - - logical :: debug = true - !---------------------------------------------- - type(lilac_init_data_t) :: lilac_init_data - type(lilac_clock_data_t) :: lilac_clock_data - class(lilac_t) :: lilac - - ! - ! Initialize the driver - ! - call setup_demo_driver_clock(driver_clock) - - ! - ! Initialize lilac - ! - - ! Hard code values normally supplied by the driver - call MPI_Comm_Dup(MPI_COMM_WORLD, lilac_init_data%mpicom_lilac, ierr) - call MPI_Comm_Dup(MPI_COMM_WORLD, lilac_init_data%mpicom_component, ierr) - call MPI_Comm_Dup(MPI_COMM_WORLD, lilac_init_data%mpicom_global_shared, ierr) - lilac_init_data%output_unit_lilac = 250 - lilac_init_data%output_unit_component = 251 - lilac_init_data%output_unit_global_shared = 252 - - lilac_init_data%component_name = MODEL_NAME_CTSM - - ! FIXME(bja, 2018-02) use namelist so the demo driver can serve as a test driver - lilac_clock_data%calendar_is_leap = .false. - lilac_clock_data%start_year = 2000 - lilac_clock_data%start_month = 1 - lilac_clock_data%start_day = 1 - lilac_clock_data%start_seconds = 0 - lilac_clock_data%stop_year = 2000 - lilac_clock_data%stop_month = 1 - lilac_clock_data%stop_day = 5 - lilac_clock_data%stop_seconds = 0 - lilac_clock_data%timestep_seconds = 3600 - - call lilac%Init(lilac_init_data, lilac_clock_data, debug) - - ! FIXME(bja, 2018-02) don't want to use the cdata structure, but we still - ! need to provide this information to the component?! - - !--- set mpicom and cdata memory - cdata%name = 'cdata_lilac' - cdata%ID = ID_lilac - cdata%mpicom = mpicom_lilac - cdata%dom => dom_lnd - cdata%gsmap => gsmap_lnd - cdata%infodata => infodata - - !--- set case information - case_name = 'lilac' - case_desc = 'lilac with clm' - model_version = 'lilac-v0.1' - hostname = 'undefined' - username = 'undefined' - start_type = 'startup' - brnch_retain_casename = .true. - single_column = .false. - scmlat = 0.0 - scmlon = 0.0 - atm_aero = .true. - call seq_infodata_putData(infodata, case_name=case_name, & - case_desc=case_desc, single_column=single_column, & - scmlat=scmlat, scmlon=scmlon, & - brnch_retain_casename=brnch_retain_casename, & - start_type=start_type, model_version=model_version, & - hostname=hostname, username=username, & - atm_aero=atm_aero ) - - !---------------------------------------------- - !--- lnd_init --- - !---------------------------------------------- - - !---------------------------------------------- - !--- atm and atm/lnd coupling init --- - !---------------------------------------------- - - !---------------------------------------------- - !--- Time Loop --- - !---------------------------------------------- - - call ESMF_ClockGet(driver_clock, currTime=driver_current_time, rc=rc) - do while (driver_current_time < driver_stop_time) - call ESMF_ClockAdvance(driver_clock, rc=rc) - call ESMF_ClockGet(driver_clock, currTime=driver_current_time, rc=rc) - call ESMF_TimeGet(driver_current_time, yy=yy, mm=mm, dd=dd, s=sec, rc=rc ) - write(iunit,'(1x,2a,4i6)') subname,'lilac demo driver ymds=', yy, mm, dd, sec - call shr_sys_flush(iunit) - - ! can manually override the alarms as needed - call ESMF_AlarmRingerOff(EAlarm_rest, rc=rc) - if (mod(dd, 5)==0 .and. sec==0) call ESMF_AlarmRingerOn(EAlarm_rest, rc) - - ! set the coupling data that is sent to the land model, this is on atm decomp - ! this is just sample test data - x2l_a%rAttr(:,:) = 0.0 - x2l_a%rAttr(idx_Sa_z ,:) = 30.0 ! m - x2l_a%rAttr(idx_Sa_u ,:) = 0.0 ! m/s - x2l_a%rAttr(idx_Sa_v ,:) = 0.0 ! m/s - x2l_a%rAttr(idx_Sa_tbot ,:) = 280.0 ! degK - x2l_a%rAttr(idx_Sa_ptem ,:) = 280.0 ! degK - x2l_a%rAttr(idx_Sa_shum ,:) = 0.0004 ! kg/kg - x2l_a%rAttr(idx_Sa_pbot ,:) = 100100.0 ! Pa - x2l_a%rAttr(idx_Faxa_rainc,:) = 4.0e-8 ! kg/m2s - x2l_a%rAttr(idx_Faxa_rainl,:) = 3.0e-8 ! kg/m2s - x2l_a%rAttr(idx_Faxa_snowc,:) = 1.0e-8 ! kg/m2s - x2l_a%rAttr(idx_Faxa_snowl,:) = 2.0e-8 ! kg/m2s - x2l_a%rAttr(idx_Faxa_lwdn ,:) = 200.0 ! W/m2 - x2l_a%rAttr(idx_Faxa_swndr,:) = 100.0 ! W/m2 - x2l_a%rAttr(idx_Faxa_swvdr,:) = 90.0 ! W/m2 - x2l_a%rAttr(idx_Faxa_swndf,:) = 20.0 ! W/m2 - x2l_a%rAttr(idx_Faxa_swvdf,:) = 40.0 ! W/m2 - - ! rearrange data to land decomposition - call mct_rearr_rearrange(x2l_a, x2l, rearr_atm2lnd) - - ! diagnose - write(iunit,*) subname,' x2l fields: ', yy, mm, dd, sec - ! call diag_avect(x2l_a, mpicom_lilac,'x2l_a') - call diag_avect(x2l, mpicom_lilac,'x2l') - - ! run clm - write(iunit,*) subname,' call lnd_run_mct', yy, mm, dd, sec - call lnd_run_mct(Eclock, cdata, x2l, l2x) - - ! rearrange data from land decomposition - call mct_rearr_rearrange(l2x, l2x_a, rearr_lnd2atm) - - ! diagnose - write(iunit,*) subname,' l2x fields: ', yy, mm, dd, sec - call diag_avect(l2x, mpicom_lilac,'l2x') - ! call diag_avect(l2x_a, mpicom_lilac,'l2x_a') - enddo - - lilac%Shutdown() - -contains - !====================================================================== - - SUBROUTINE diag_avect(av, mpicom, comment) - - use seq_infodata_mod - - implicit none - - ! !INPUT/OUTPUT PARAMETERS: - - type(mct_aVect) , intent(in) :: av - integer , intent(in) :: mpicom - character(len=*), intent(in) :: comment - - !--- local --- - integer :: n, k ! counters - integer :: npts, nptsg ! number of local/global pts in AV - integer :: kflds ! number of fields in AV - real*8, pointer :: sumbuf (:) ! sum buffer - real*8, pointer :: sumbufg(:) ! sum buffer reduced - integer :: iam ! pe number - type(mct_string) :: mstring ! mct char type - character(len=128):: itemc ! string converted to char - - !----- formats ----- - character(*), parameter :: subName = '(diag_avect) ' - - !---------------------------------------------------------------- - - npts = mct_aVect_lsize(AV) - kflds = mct_aVect_nRattr(AV) - allocate(sumbuf(kflds), sumbufg(kflds)) - - sumbuf = 0.0 - - do k = 1, kflds - do n = 1, npts - sumbuf(k) = sumbuf(k) + (AV%rAttr(k, n)) - enddo - enddo - - call MPI_REDUCE(sumbuf, sumbufg, kflds, MPI_REAL8, MPI_SUM, 0, mpicom, ierr) - call MPI_COMM_RANK(mpicom, iam, ierr) - - if (iam == 0) then - do k = 1, kflds - call mct_aVect_getRList(mstring, k, AV) - itemc = mct_string_toChar(mstring) - call mct_string_clean(mstring) - write(iunit, 101) trim(comment), k, sumbufg(k), trim(itemc) - enddo - call shr_sys_flush(iunit) - endif - - deallocate(sumbuf, sumbufg) - -101 format('comm_diag ', a, 1x, i3, es26.19, 1x, a) - - end subroutine diag_avect - - !====================================================================== - - subroutine setup_demo_driver_clocks() - - implicit none - - - - end subroutine setup_demo_driver_clocks - -end program lilac_demo_driver - diff --git a/lilac/src/lilac.F90 b/lilac/src/lilac.F90 deleted file mode 100644 index 9f89e97d9f..0000000000 --- a/lilac/src/lilac.F90 +++ /dev/null @@ -1,399 +0,0 @@ -module lilac - ! - ! Public interface to lilac - ! - - implicit none - - private - - integer, parameter :: LILAC_MASTER_PROC = 0 - integer, parameter :: LILAC_NUM_COMPONENTS = 1 - - type, public :: lilac_t - private - character(len=STRING_32) :: component_name - logical :: debug - - integer :: mpicom_lilac - integer :: my_mpi_rank_lilac - integer :: num_mpi_tasks_lilac - integer :: mct_comp_id - - type(ESMF_Clock) :: lilac_clock - type(ESMF_Time) :: start_time - type(ESMF_Time) :: stop_time - type(ESMF_TimeInterval) :: time_step - type(ESMF_Alarm) :: alarm_stop - type(ESMF_Alarm) :: alarm_restart - - contains - ! Public API - procedure, public :: Init => lilac_init - procedure, public :: Shutdown => lilac_shutdown - procudure, public :: AdvanceTime => lilac_advance_time - - ! private initialization routines - procedure, private :: lilac_init_parallel - procedure, private :: lilac_init_logging - procedure, private :: lilac_init_io - procedure, private :: lilac_init_clocks - procedure, private :: lilac_init_fields - procedure, private :: lilac_init_orbit - procedure, private :: lilac_init_land - procedure, private :: lilac_init_coupling - - ! private shudown routines - procedure, private :: lilac_shutdown_land - procedure, private :: lilac_shutdown_parallel - - end type lilac_t - - - -contains - - ! - ! Public API - ! - subroutine lilac_init(this, init_data, clock_data, debug) - - use lilac_api_types, only : lilac_clock_data_t - use mct_mod, only : mct_world_init - - implicit none - - class(lilac_t), intent(inout) :: this - type(lilac_init_data_t), intent(in) :: init_data - type(lilac_clock_data_t), intent(in) :: clock_data - logical, intent(in) :: debug - - this%debug = debug - this%component_name = init_data%component_name - - call this%lilac_init_parallel(init_data%mpicom_lilac, & - init_data%mpicom_component, init_data%mpicom_global_shared) - - call this%lilac_init_logging(init_data%output_unit_lilac, init_data%output_unit_component) - call this%lilac_init_io() - call this%lilac_init_clocks(clock_data) - ! TODO(bja, 2018-03) use init_data%component_name to do some model - ! specific setup, including getting a list of hard coded input and output - ! exchange fields. - call this%lilac_init_fields() - call this%lilac_init_orbit() - call this%lilac_init_land() - call this%lilac_init_coupling() - - end subroutine lilac_init - - subroutine lilac_advance_time(this) - - implicit none - - class(lilac_t), intent(inout) :: this - - end subroutine lilac_advance_time - - subroutine lilac_shutdown(this) - - implicit none - - class(lilac_t), intent(inout) :: this - - if (this%my_mpi_rank_lilac == LILAC_MASTER_PROC) then - write(this%output_unit, *) 'Shutting down lilac interface for component ', this%component_name, ' ...' - end if - - call shr_sys_flush(this%output_unit) - - call this%lilac_shutdown_land() - call this%lilac_shutdown_parallel() - - if (this%my_mpi_rank_lilac == LILAC_MASTER_PROC) then - write(this%output_unit, *) 'lilac shut down for component ', this%component_name, ' complete.' - end if - - end subroutine lilac_shutdown - - ! - ! Private work functions - ! - subroutine lilac_init_parallel(this, mpicom_lilac, mpicom_component, mpicom_global_shared) - ! Initialize parallel components, e.g. MPI, MCT - - implicit none - - class(lilac_t), intent(inout) :: this - - ! should be safe if previously initialized - call MPI_Init(ierr) - - this%mpicom_lilac = mpicom_lilac - this%mpicom_component = mpicom_component - - call MPI_COMM_RANK(this%mpicom_lilac, this%my_lilac_mpi_rank, ierr) - call MPI_COMM_SIZE(this%mpicom_lilac, this%num_lilac_mpi_tasks, ierr) - - ! FIXME(bja, 2018-03) 1 (component | lilac) or two (component & lilac)? - this%mct_num_comps = 1 - this%mct_comp_id = 1 - ! NOTE(bja, 2018-02) MPI_COMM_WORLD should eventually be initialized on - ! the union of the lilac and component communicators! If 2, then need arrays?! - call mct_world_init(this%mct_num_comps, MPI_COMM_WORLD, this%mpicom_lilac, this%mct_comp_id) - - end subroutine lilac_init_parallel - - subroutine lilac_init_logging(this, output_unit_lilac, output_unit_global_shared) - - implicit none - - class(lilac_t), intent(inout) :: this - - character(len=*), parameter :: subname = 'lilac_init_logging' - ! open logfile for lilac - - this%output_unit = output_unit_lilac - - ! FIXME(bja, 2018-03) do we want a single shared log file, or one per rank? - write(log_file_name,'(a,i4.4)') 'lilac.log.', this%my_mpi_rank_lilac - open(this%output_unit, file=trim(log_file_name)) - if (this%my_mpi_rank_lilac == LILAC_MASTER_PROC) then - write(this%output_unit, *) subname, ': Starting lilac interface for component: ', this%component_name - write(this%output_unit, *) subname, ': num lilac tasks = ', this%num_mpi_tasks_lilac - write(this%output_unit, *) subname, ': my mpi rank = ', this%my_mpi_rank_lilac - write(this%output_unit, *) subname, ': mct component ID = ', this%mct_comp_id_lilac - call shr_sys_flush(this%output_unit) - end if - - ! NOTE(bja, 2018-02) these are setting global variables within the shr code! - call shr_file_setLogUnit(output_unit_global_shared) - call shr_file_setLogLevel(1) - - end subroutine lilac_init_logging - - subroutine lilac_init_io(this) - ! NOTE(bja, 2018-02) There is only a *single science component* in each - ! lilac instance. For now assuming just the science component interacts - ! with pio, but lilac may have some parallel data I/O needs. If so it - ! needs to be added to these data structures! - - implicit none - - class(lilac_t), intent(inout) :: this - - ! - call shr_pio_init1(LILAC_NUM_COMPONENTS, 'pio_in', this%mpicom_lilac) - allocate( & - comp_id(LILAC_NUM_COMPONENTS), & - comp_name(LILAC_NUM_COMPONENTS), & - comp_iamin(LILAC_NUM_COMPONENTS), & - comp_comm(LILAC_NUM_COMPONENTS), & - comp_comm_iam(LILAC_NUM_COMPONENTS)) - - index = 1 - comp_id(index) = 1 - comp_name(index) = MODEL_NAME_LILAC // '_' // trim(this%component_name) - comp_iamin(index) = .true. - comp_comm(index) = this%mpicom_lilac - comp_comm_iam(index) = this%my_mpi_rank_lilac - - ! TODO(bja, 2018-03) Never have more than one science component, remove loop? - do n = 1, LILAC_NUM_COMPONENTS - index = index + n - comp_id(index) = ID_component - comp_name(index) = this%component_name - comp_iamin(index) = .true. - comp_comm(index) = this%mpicom_component - comp_comm_iam(index) = mytask ! FIXME(bja, 2018-02) when land and lilac are on different comms?? - enddo - - call shr_pio_init2(comp_id, comp_name, comp_iamin, comp_comm, comp_comm_iam) - - deallocate(comp_id, comp_name, comp_iamin, comp_comm, comp_comm_iam) - - end subroutine lilac_init_io - - subroutine lilac_init_clocks(this, clock_data) - - use ESMF - - implicit none - - class(lilac_t), intent(inout) :: this - type(lilac_clock_data_t), intent(in) :: clock_data - - type(ESMF_Calendar), target :: calendar ! FIXME(bja, 2018-02) does not need to be freed?! - integer :: cal_kind_flag - integer :: year, month, day, sec - - if (clock_data%calendar_is_leap == .false.) then - cal_kind_flag = ESMF_CALKIND_NOLEAP - else - ! FIXME(bja, 2018-03) not implemented error! ESMF_CALKIND_GREGORIAN? - end if - - if (ESMF_IsInitialized() /= .true.) then - ! NOTE(bja, 2018-03) allocates and operates on global data! - call ESMF_Initialize(rc=rc) - end if - - calendar = ESMF_CalendarCreate( name='lilac', calkindflag=cal_kind_flag, rc=rc ) - call ESMF_TimeSet(this%start_time, yy=clock_data%start_year, mm=clock_data%start_month, & - dd=clock_data%start_day, s=clock_data%start_seconds, calendar=calendar, rc=rc) - - call ESMF_TimeSet(this%stop_time , yy=clock_data%stop_year, mm=clock_data%stop_month, & - dd=clock_data%stop_day, s=clock_data%stop_seconds, calendar=calendar, rc=rc) - - call ESMF_TimeIntervalSet(this%time_step, s=clock_data%time_step_seconds, rc=rc) - - this%lilac_clock = ESMF_ClockCreate(name='lilac_clock', & - TimeStep=this%time_step, startTime=this%start_time, & - RefTime=this%start_time, stopTime=this%stop_time, rc=rc) - - this%alarm_stop = ESMF_AlarmCreate(name='alarm_stop' , & - clock=this%lilac_clock, ringTime=this%stop_time, rc=rc) - this%alarm_rest = ESMF_AlarmCreate(name='alarm_restart', & - clock=this%lilac_clock, ringTime=this%stop_time, rc=rc) - - if (this%debug .and. this%my_mpi_rank_lilac == LILAC_MASTER_PROC) then - call ESMF_TimeGet( start_time, yy=year, mm=month, dd=day, s=sec, rc=rc ) - write(this%output_unit, '(1x,2a,4i6)') subname,': start time ymds=', year, month, day, sec - call ESMF_TimeGet( stop_time, yy=year, mm=month, dd=day, s=sec, rc=rc ) - write(this%output_unit, '(1x,2a,4i6)') subname,': stop time ymds=', year, month, day, sec - call shr_sys_flush(this%output_unit) - end if - - end subroutine lilac_init_clocks - - subroutine lilac_init_fields(this) - ! Set coupling fields. - - implicit none - - class(lilac_t), intent(inout) :: this - - ! FIXME(bja, 2018-02) this should be dynamically created at runtime - ! instead of hard coded! - - seq_flds_dom_coord='lat:lon' - seq_flds_dom_other='area:aream:mask:frac' - seq_flds_dom_fields=trim(seq_flds_dom_coord)//':'//trim(seq_flds_dom_other) - - seq_flds_x2l_states= 'Sa_z:Sa_u:Sa_v:Sa_tbot:Sa_ptem:Sa_shum:Sa_pbot:Sg_icemask:Sg_icemask_coupled_fluxes' - seq_flds_x2l_fluxes= 'Faxa_rainc:Faxa_rainl:Faxa_snowc:Faxa_snowl:Faxa_lwdn:Faxa_swndr:Faxa_swvdr:Faxa_swndf:Faxa_swvdf:Faxa_bcphidry:Faxa_bcphodry:Faxa_bcphiwet:Faxa_ocphidry:Faxa_ocphodry:Faxa_ocphiwet:Faxa_dstwet1:Faxa_dstwet2:Faxa_dstwet3:Faxa_dstwet4:Faxa_dstdry1:Faxa_dstdry2:Faxa_dstdry3:Faxa_dstdry4:Flrr_flood:Flrr_volr' - seq_flds_x2l_fields= trim(seq_flds_x2l_states)//':'//trim(seq_flds_x2l_fluxes) - - seq_flds_l2x_states= 'Sl_avsdr:Sl_anidr:Sl_avsdf:Sl_anidf:Sl_tref:Sl_qref:Sl_t:Sl_fv:Sl_ram1:Sl_snowh:Sl_u10' - seq_flds_l2x_fluxes= 'Fall_swnet:Fall_taux:Fall_tauy:Fall_lat:Fall_sen:Fall_lwup:Fall_evap:Fall_flxdst1:Fall_flxdst2:Fall_flxdst3:Fall_flxdst4:Flrl_rofl:Flrl_rofi:Fall_voc001:Fall_voc002:Fall_voc003:Fall_voc004:Fall_voc005:Fall_voc006:Fall_voc007:Fall_voc008' - seq_flds_l2x_fields= trim(seq_flds_l2x_states)//':'//trim(seq_flds_l2x_fluxes) - - - end subroutine lilac_init_fields - - subroutine lilac_init_orbit(this) - - implicit none - - class(lilac_t), intent(inout) :: this - - !--- set orbital params - orb_iyear = 1990 - call shr_orb_params(orb_iyear, orb_eccen, orb_obliq, orb_mvelp, & - orb_obliqr, orb_lambm0, orb_mvelpp, .true.) - call seq_infodata_putData(infodata, orb_eccen=orb_eccen, orb_mvelpp=orb_mvelpp, & - orb_lambm0=orb_lambm0, orb_obliqr=orb_obliqr ) - - - end subroutine lilac_init_orbit - - subroutine lilac_init_land(this) - - implicit none - - write(this%output_unit,*) subname,' calling lnd_init_mct' - call shr_sys_flush(this%output_unit) - call lnd_init_mct(Eclock, cdata, x2l, l2x) - - call diag_avect(l2x, mpicom_lilac,'l2x_init') - - idx_Sa_z = mct_avect_indexra(x2l,'Sa_z') - idx_Sa_u = mct_avect_indexra(x2l,'Sa_u') - idx_Sa_v = mct_avect_indexra(x2l,'Sa_v') - idx_Sa_tbot = mct_avect_indexra(x2l,'Sa_tbot') - idx_Sa_ptem = mct_avect_indexra(x2l,'Sa_ptem') - idx_Sa_shum = mct_avect_indexra(x2l,'Sa_shum') - idx_Sa_pbot = mct_avect_indexra(x2l,'Sa_pbot') - idx_Faxa_rainc = mct_avect_indexra(x2l,'Faxa_rainc') - idx_Faxa_rainl = mct_avect_indexra(x2l,'Faxa_rainl') - idx_Faxa_snowc = mct_avect_indexra(x2l,'Faxa_snowc') - idx_Faxa_snowl = mct_avect_indexra(x2l,'Faxa_snowl') - idx_Faxa_lwdn = mct_avect_indexra(x2l,'Faxa_lwdn') - idx_Faxa_swndr = mct_avect_indexra(x2l,'Faxa_swndr') - idx_Faxa_swvdr = mct_avect_indexra(x2l,'Faxa_swvdr') - idx_Faxa_swndf = mct_avect_indexra(x2l,'Faxa_swndf') - idx_Faxa_swvdf = mct_avect_indexra(x2l,'Faxa_swvdf') - - end subroutine lilac_init_land - - subroutine lilac_init_coupling(this) - - implicit none - - class(lilac_t), intent(inout) :: this - - ! set atm grid size to land grid size in this example. for a real - ! atmosphere model, the atm and land grids should agree at the outset. - call seq_infodata_getData(infodata, lnd_nx=atm_nx, lnd_ny=atm_ny) - - ! atm decomp - gstart = ((mytask * atm_nx * atm_ny) / ntasks) + 1 - gend = (((mytask+1) * atm_nx * atm_ny) / ntasks) - lsize = gend - gstart + 1 - gsize = atm_nx * atm_ny - allocate(gindex(lsize)) - do n = gstart, gend - m = n-gstart+1 - gindex(m) = n - end do - write(this%output_unit,'(1x,2a,5i8)') subname,' atm decomp = ', mytask, gsize, lsize, gstart, gend - - ! initialize land grid on atm decomp - call mct_gsMap_init(gsmap_atm, gindex, mpicom_lilac, ID_lilac, lsize, gsize) - deallocate(gindex) - - ! initialize rearrangers between atm and land decomps - call mct_rearr_init(gsmap_atm, gsmap_lnd, mpicom_lilac, rearr_atm2lnd) - call mct_rearr_init(gsmap_lnd, gsmap_atm, mpicom_lilac, rearr_lnd2atm) - - ! initialize atm avects from land avects with atm lsize - call mct_avect_init(x2l_a, x2l, lsize) - call mct_avect_zero(x2l_a) - call mct_avect_init(l2x_a, l2x, lsize) - call mct_avect_zero(l2x_a) - - end subroutine lilac_init_coupling - - subroutine lilac_shutdown_land(this) - - implicit none - - class(lilac_t), intent(inout) :: this - - write(this%output_unit, *) 'lilac shutting down component ', this%comp_name - call lnd_final_mct(Eclock, cdata, x2l, l2x) - - end subroutine lilac_shutdown_land - - subroutine lilac_shutdown_parallel(this) - - implicit none - - class(lilac_t), intent(inout) :: this - - ! FIXME(bja, 2018-02) need to determine if it is our responsibility to shutdown mpi or the caller!? - ! call MPI_Finalize(ierr) - - end subroutine lilac_shutdown_parallel - -end module lilac diff --git a/lilac/src/lilac_api_types.F90 b/lilac/src/lilac_api_types.F90 deleted file mode 100644 index da66aefe04..0000000000 --- a/lilac/src/lilac_api_types.F90 +++ /dev/null @@ -1,43 +0,0 @@ -module lilac_api_types - - implicit none - - use lilac_constants, only : STRING_128 - -contains - - type :: lilac_init_data_t - character(len=STRING_32) :: component_name - integer :: mpicom_lilac - integer :: mpicom_component - integer :: output_unit_lilac - integer :: output_unit_global_shared ! this should be the same for all instances of lilac! - integer :: output_unit_component - - end type lilac_init_data_t - - type :: lilac_clock_data_t - logical :: calendar_is_leap - integer :: start_year - integer :: start_month - integer :: start_day - integer :: start_second ! seconds since midnight - - integer :: stop_year - integer :: stop_month - integer :: stop_day - integer :: stop_second ! seconds since midnight - - integer :: time_step_seconds - end type lilac_clock_data_t - - - type :: lilac_exchange_fields_t - character(len=STRING_128) :: long_name - character(len=STRING_128) :: short_name - character(len=STRING_128) :: field_name - character(len=STRING_128) :: units - integer :: field_type - end type lilac_exchange_fields_t - -end module lilac_api_types diff --git a/lilac/src/lilac_constants.F90 b/lilac/src/lilac_constants.F90 deleted file mode 100644 index d9e6442206..0000000000 --- a/lilac/src/lilac_constants.F90 +++ /dev/null @@ -1,18 +0,0 @@ -module lilac_constants - - implicit none - -contains - - integer, parameter :: STRING_128 = 128 - integer, parameter :: STRING_32 = 32 - - integer, parameter :: FIELD_TYPE_INTEGER = 0 - integer, parameter :: FIELD_TYPE_REAL_8BYTE = 1 - - ! known models names - character(len=*), parameter :: MODEL_NAME_LILAC = 'lilac' - character(len=*), parameter :: MODEL_NAME_CTSM = 'ctsm' - character(len=*), parameter :: MODEL_NAME_TEST = 'test' - -end module lilac_constants diff --git a/lilac/src/stub_comp_mct.F90 b/lilac/src/stub_comp_mct.F90 deleted file mode 100644 index de792d6c58..0000000000 --- a/lilac/src/stub_comp_mct.F90 +++ /dev/null @@ -1,8 +0,0 @@ -module stub_comp_mct - - implicit none - -contains - - -end module stub_comp_mct diff --git a/lilac/tests/CMakeLists.txt b/lilac/tests/CMakeLists.txt new file mode 100644 index 0000000000..6361d9e9c2 --- /dev/null +++ b/lilac/tests/CMakeLists.txt @@ -0,0 +1,2 @@ +# Add tests here +add_subdirectory(hello_world) diff --git a/lilac/tests/hello_world/CMakeLists.txt b/lilac/tests/hello_world/CMakeLists.txt new file mode 100644 index 0000000000..0e5fbd44bc --- /dev/null +++ b/lilac/tests/hello_world/CMakeLists.txt @@ -0,0 +1,3 @@ +file(GLOB_RECURSE SOURCES *.f90 *.h) +add_executable("test_hello_world" ${SOURCES} ) +target_link_libraries(lilac) diff --git a/lilac/tests/hello_world/main.f90 b/lilac/tests/hello_world/main.f90 new file mode 100644 index 0000000000..78312075c1 --- /dev/null +++ b/lilac/tests/hello_world/main.f90 @@ -0,0 +1,21 @@ +program main + + ! modules + use ESMF + ! use lilac, ONLY : lilac_init + + implicit none + + ! local variables + integer:: rc + + ! call lilac_init() + ! TODO fix linking with lilac + call ESMF_Initialize(rc=rc) + if (rc /= ESMF_SUCCESS) call ESMF_Finalize(endflag=ESMF_END_ABORT) + + print *, "Hello LILAC World" + + call ESMF_Finalize() + +end program main diff --git a/lilac/tests/rand_atm_rand_lnd/CmakeLists.txt b/lilac/tests/rand_atm_rand_lnd/CmakeLists.txt new file mode 100644 index 0000000000..c7c253746b --- /dev/null +++ b/lilac/tests/rand_atm_rand_lnd/CmakeLists.txt @@ -0,0 +1,4 @@ + +file(GLOB TEST_SOURCES *.f90 *.h) +add_executable(rand_atm_rand_land ${TEST_SOURCES} ) +target_link_libraries(rand_atm_rand_land) diff --git a/lilac/tests/rand_atm_rand_lnd/main.f90 b/lilac/tests/rand_atm_rand_lnd/main.f90 new file mode 100644 index 0000000000..f0c343cc4f --- /dev/null +++ b/lilac/tests/rand_atm_rand_lnd/main.f90 @@ -0,0 +1,5 @@ +program main + + + +end program main