diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 00000000..f8dd6ae2 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,21 @@ +version: 2 + +sphinx: + builder: html + configuration: docs/source/conf.py + fail_on_warning: false + +formats: + - htmlzip + +python: + version: 3.8 + install: + - requirements: requirements-docs.txt + - requirements: requirements.txt + - method: pip + path: . + system_packages: false + +build: + image: latest diff --git a/.travis.yml b/.travis.yml index 0848b28e..1144bfb1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,23 @@ env: matrix: - PYTHON="3.8" sudo: false + +jobs: + include: + - stage: lint + os: linux + script: + - pip install flake8 + - make lint + + - stage: docs + os: linux + install: + - pip install -r requirements-docs.txt + - pip install -e . + script: + - make -C docs clean html + before_install: - | if [[ $TRAVIS_OS_NAME == "osx" ]]; then diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst new file mode 100644 index 00000000..09d870ee --- /dev/null +++ b/CONTRIBUTING.rst @@ -0,0 +1,131 @@ +.. highlight:: shell + +============ +Contributing +============ + +Contributions are welcome, and they are greatly appreciated! Every little bit +helps, and credit will always be given. + +You can contribute in many ways: + +Types of Contributions +---------------------- + +Report Bugs +~~~~~~~~~~~ + +Report bugs at https://github.com/csdms/babelizer/issues. + +If you are reporting a bug, please include: + +* Your operating system name and version. +* Any details about your local setup that might be helpful in troubleshooting. +* Detailed steps to reproduce the bug. + +Fix Bugs +~~~~~~~~ + +Look through the GitHub issues for bugs. Anything tagged with "bug" and "help +wanted" is open to whoever wants to implement it. + +Implement Features +~~~~~~~~~~~~~~~~~~ + +Look through the GitHub issues for features. Anything tagged with "enhancement" +and "help wanted" is open to whoever wants to implement it. + +Write Documentation +~~~~~~~~~~~~~~~~~~~ + +*babelizer* could always use more documentation, whether as part of the +official *babelizer* docs, in docstrings, or even on the web in blog posts, +articles, and such. + +Submit Feedback +~~~~~~~~~~~~~~~ + +The best way to send feedback is to file an issue at https://github.com/csdms/babelizer/issues. + +If you are proposing a feature: + +* Explain in detail how it would work. +* Keep the scope as narrow as possible, to make it easier to implement. +* Remember that this is a volunteer-driven project, and that contributions + are welcome :) + +Get Started! +------------ + +Ready to contribute? Here's how to set up *babelizer* for local development. + +1. Fork the *babelizer* repo on GitHub. +2. Clone your fork locally:: + + $ git clone git@github.com:your_name_here/babelizer.git + +3. Install your local copy into a conda environment. Assuming you have conda + installed, this is how you set up your fork for local development:: + + $ conda create -n babelizer python=3.8 + $ conda activate babelizer + $ cd babelizer/ + $ conda install --file=requirements.txt + + $ python setup.py develop + +4. Create a branch for local development:: + + $ git checkout -b name-of-your-bugfix-or-feature + + Now you can make your changes locally. + +5. When you're done making changes, check that your changes pass flake8 and the + tests:: + + $ make lint + $ make test + + To get flake8, just conda install it into your environment. + +6. Commit your changes and push your branch to GitHub:: + + $ git add . + $ git commit -m "Your detailed description of your changes." + $ git push origin name-of-your-bugfix-or-feature + +7. Submit a pull request through the GitHub website. + +Pull Request Guidelines +----------------------- + +Before you submit a pull request, check that it meets these guidelines: + +1. The pull request should include tests. +2. If the pull request adds functionality, the docs should be updated. Put + your new functionality into a function with a docstring, and add the + feature to the list in README.rst. +3. The pull request need only work with Python >= 3.8. Check + https://travis-ci.org/csdms/babelizer/pull_requests + and make sure that the tests pass. + +Deploying +--------- + +A reminder for the maintainers on how to deploy. To make a new release, +you will need to have [zest.releaser](https://zestreleaser.readthedocs.io/en/latest/) +installed, which can be installed with *pip*, + +.. code:: bash + + $ pip install zest.releaser[recommended] + +Make sure all your changes are committed (including an entry in CHANGES.rst). +Then run, + +.. code:: bash + + $ fullrelease + +This will create a new tag and alert the *babelizer* feedstock on +*conda-forge* that there is a new release. diff --git a/LICENSE b/LICENSE deleted file mode 100644 index e3dbc24a..00000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2018 Community Surface Dynamics Modeling System - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/LICENSE.rst b/LICENSE.rst new file mode 100644 index 00000000..a01c8436 --- /dev/null +++ b/LICENSE.rst @@ -0,0 +1,26 @@ +The MIT License (MIT) +===================== + +Copyright © `2020` `Community Surface Dynamics Modeling System` + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + diff --git a/MANIFEST.in b/MANIFEST.in index 42b7ef92..dc977905 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,3 @@ -include LICENSE include *.rst include requirements*.txt include Makefile diff --git a/README.rst b/README.rst index 0b2addcb..fa127667 100644 --- a/README.rst +++ b/README.rst @@ -1,6 +1,10 @@ .. image:: https://img.shields.io/travis/csdms/babelizer.svg :target: https://travis-ci.org/csdms/babelizer +.. image:: https://readthedocs.org/projects/babelizer/badge/?version=latest + :target: https://babelizer.readthedocs.io/en/latest/?badge=latest + :alt: Documentation Status + .. image:: https://img.shields.io/badge/code%20style-black-000000.svg :target: https://github.com/csdms/babelizer @@ -15,8 +19,8 @@ Wrap BMI libraries with Python bindings About ***** -The *babelizer* is a utility for wrapping libraries, from a variety for -languages, that expose a Basic Model Interface (BMI) so that they can +The *babelizer* is a utility for wrapping libraries, from a variety of +languages, that expose a Basic Model Interface (BMI) so that they can be imported as a Python package. @@ -30,7 +34,7 @@ Supported languages: Requirements ************ -The *babelizer* is Python 3 only. +The *babelizer* requires Python >=3.8. Apart from Python, the *babelzer* has a number of other requirements, all of which @@ -52,20 +56,19 @@ Installation To install the *babelizer*, first create a new environment in which *babelizer* will be installed. This, although not necessary, will isolate the installation so that there won't be conflicts with your -base *Python* installation. This can be done with *conda* as:: +base *Python* installation. This can be done with *conda* as, + +.. code:: bash - $ conda create -n babelizer python=3 - $ conda activate babelizer + $ conda create -n babelizer python=3 + $ conda activate babelizer Stable Release ============== -The *babelizer*, and its dependencies, can be installed either with *pip* -or *conda*. Using *pip*:: - - $ pip install babelizer +The *babelizer*, and its dependencies, is best installed with *conda*, -Using *conda*:: +.. code:: bash $ conda install babelizer -c conda-forge @@ -257,11 +260,15 @@ For example the above *babel.toml* can be generated with the following, .. code:: bash - $ babelize generate babel.toml --summary="PyMT plugin for hydrotrend" --entry-point=Hydrotrend=bmi_hydrotrend:register_bmi_hydrotrend --name=hydrotrend --requirement=hydrotrend + $ babelize generate babel.toml \ + --summary="PyMT plugin for hydrotrend" \ + --entry-point=Hydrotrend=bmi_hydrotrend:register_bmi_hydrotrend \ + --name=hydrotrend \ + --requirement=hydrotrend -******** -Examples -******** +*** +Use +*** Generate Python bindings for a library that implements a BMI, sending output to the current directory @@ -275,3 +282,10 @@ Update an existing repository .. code:: bash $ babelize update + +For a complete example of using the *babelizer* +to wrap a C library exposing a BMI, +see the User Guide of the `documentation`_. + + +.. _documentation: https://babelizer.readthedocs.io/ diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 00000000..d0c3cbf1 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +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/make.bat b/docs/make.bat new file mode 100644 index 00000000..6247f7e2 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=source +set BUILDDIR=build + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/source/_static/babelizer-logo-lowercase.png b/docs/source/_static/babelizer-logo-lowercase.png new file mode 100644 index 00000000..4dae1339 Binary files /dev/null and b/docs/source/_static/babelizer-logo-lowercase.png differ diff --git a/docs/source/_static/powered-by-logo-header.png b/docs/source/_static/powered-by-logo-header.png new file mode 100644 index 00000000..46c82d07 Binary files /dev/null and b/docs/source/_static/powered-by-logo-header.png differ diff --git a/docs/source/_templates/links.html b/docs/source/_templates/links.html new file mode 100644 index 00000000..dfe571d3 --- /dev/null +++ b/docs/source/_templates/links.html @@ -0,0 +1,7 @@ +

Useful Links

+ + diff --git a/docs/source/_templates/sidebarintro.html b/docs/source/_templates/sidebarintro.html new file mode 100644 index 00000000..cec7240a --- /dev/null +++ b/docs/source/_templates/sidebarintro.html @@ -0,0 +1,6 @@ +

About babelizer

+

+ The babelizer wraps libraries + that expose a Basic Model Interface (BMI) + so they can imported as Python packages. +

diff --git a/docs/source/api/babelizer.rst b/docs/source/api/babelizer.rst new file mode 100644 index 00000000..3ee00f59 --- /dev/null +++ b/docs/source/api/babelizer.rst @@ -0,0 +1,61 @@ +babelizer package +================= + +Submodules +---------- + +babelizer.cli module +-------------------- + +.. automodule:: babelizer.cli + :members: + :undoc-members: + :show-inheritance: + +babelizer.errors module +----------------------- + +.. automodule:: babelizer.errors + :members: + :undoc-members: + :show-inheritance: + +babelizer.metadata module +------------------------- + +.. automodule:: babelizer.metadata + :members: + :undoc-members: + :show-inheritance: + +babelizer.render module +----------------------- + +.. automodule:: babelizer.render + :members: + :undoc-members: + :show-inheritance: + +babelizer.utils module +---------------------- + +.. automodule:: babelizer.utils + :members: + :undoc-members: + :show-inheritance: + +babelizer.wrap module +--------------------- + +.. automodule:: babelizer.wrap + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: babelizer + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/api/index.rst b/docs/source/api/index.rst new file mode 100644 index 00000000..5fbee2fd --- /dev/null +++ b/docs/source/api/index.rst @@ -0,0 +1,7 @@ +Developer Documentation +----------------------- + +.. toctree:: + :maxdepth: 2 + + modules diff --git a/docs/source/api/modules.rst b/docs/source/api/modules.rst new file mode 100644 index 00000000..811ffbe8 --- /dev/null +++ b/docs/source/api/modules.rst @@ -0,0 +1,7 @@ +babelizer +========= + +.. toctree:: + :maxdepth: 4 + + babelizer diff --git a/docs/source/authors.rst b/docs/source/authors.rst new file mode 100644 index 00000000..e2f0e1c0 --- /dev/null +++ b/docs/source/authors.rst @@ -0,0 +1 @@ +.. include:: ../../CREDITS.rst diff --git a/docs/source/babel_heatc.toml b/docs/source/babel_heatc.toml new file mode 100644 index 00000000..c5ff2e55 --- /dev/null +++ b/docs/source/babel_heatc.toml @@ -0,0 +1,23 @@ +[library] +language = "c" +entry_point = ["HeatModel=bmiheatc:register_bmi_heat"] + +[build] +undef_macros = [] +define_macros = [] +libraries = [] +library_dirs = [] +include_dirs = [] +extra_compile_args = [] + +[plugin] +name = "heatc" +requirements = [""] + +[info] +plugin_author = "csdms" +plugin_author_email = "csdms@colorado.edu" +github_username = "pymt-lab" +plugin_license = "MIT" +summary = "PyMT plugin for heat model" + diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst new file mode 100644 index 00000000..d76c92b6 --- /dev/null +++ b/docs/source/changelog.rst @@ -0,0 +1 @@ +.. include:: ../../CHANGES.rst diff --git a/docs/source/conf.py b/docs/source/conf.py new file mode 100644 index 00000000..f8eb2330 --- /dev/null +++ b/docs/source/conf.py @@ -0,0 +1,77 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- 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('.')) + + +# The master toctree document. +master_doc = 'index' + +# -- Project information ----------------------------------------------------- + +project = 'babelizer' +copyright = '2020, Community Surface Dynamics Modeling System' +author = 'Community Surface Dynamics Modeling System' + +# The full version, including alpha/beta/rc tags +release = '0.2' + + +# -- General configuration --------------------------------------------------- + +# 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.napoleon'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# 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 = [] + + +# -- 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' + +# 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'] + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +html_logo = "_static/powered-by-logo-header.png" + +# Custom sidebar templates, maps document names to template names. +html_sidebars = { + "index": [ + "sidebarintro.html", + "links.html", + "sourcelink.html", + "searchbox.html", + ], + "**": [ + "sidebarintro.html", + "links.html", + "sourcelink.html", + "searchbox.html", + ] +} diff --git a/docs/source/contributing.rst b/docs/source/contributing.rst new file mode 100644 index 00000000..ac7b6bcf --- /dev/null +++ b/docs/source/contributing.rst @@ -0,0 +1 @@ +.. include:: ../../CONTRIBUTING.rst diff --git a/docs/source/environment.yml b/docs/source/environment.yml new file mode 100644 index 00000000..e8851d0a --- /dev/null +++ b/docs/source/environment.yml @@ -0,0 +1,11 @@ +# A conda environment file for the babelizer example +name: wrap +channels: + - conda-forge +dependencies: +- python=3 +- cmake +- pkg-config +- c-compiler +- bmi-c +- babelizer diff --git a/docs/source/example.rst b/docs/source/example.rst new file mode 100644 index 00000000..f8a461ba --- /dev/null +++ b/docs/source/example.rst @@ -0,0 +1,347 @@ +Example: Wrapping a C model +=========================== + +In this example, we'll use the *babelizer* +to wrap the *heat* model from the `bmi-example-c`_ repository, +allowing it to be run in Python. +The model and its BMI are written in C. +To simplify package management in the example, +we'll use :term:`conda`. +We'll also use :term:`git` to obtain the model source code. + +This is a somewhat long example. +To break it up, +here are the steps we'll take: + +#. Create a :term:`conda environment` that includes software to compile the + model and wrap it with the *babelizer* +#. Clone the `bmi-example-c`_ repository from GitHub and build the + *heat* model from source +#. Create a *babelizer* input file describing the *heat* model +#. Run the *babelizer* to generate Python bindings, then build the bindings +#. Show the *heat* model running in Python through *pymt* + +Before we begin, +create a directory to hold our work: + +.. code:: bash + + $ mkdir build + $ cd build + + +Set up a conda environment +-------------------------- + +Start by setting up a :term:`conda environment` that includes the *babelizer*, +as well as a toolchain to build and install the model. +The necessary packages are listed in the conda environment file +:download:`environment.yml`: + +.. include:: environment.yml + :literal: + +:download:`Download ` this file +and create the new environment with: + +.. code:: bash + + $ conda env create --file=environment.yml + +When this command completes, +activate the environment: + +.. code:: bash + + $ conda activate wrap + +The *wrap* environment now contains all the dependencies needed +to build, install, and wrap the *heat* model. + + +Build the *heat* model from source +---------------------------------- + +Clone the `bmi-example-c`_ repository from GitHub: + +.. code:: bash + + $ git clone https://github.com/csdms/bmi-example-c + +There are general `instructions`_ in the repository for building and installing +this package on Linux, macOS, and Windows. +We'll augment those instructions +with the note that we're installing into the *wrap* conda environment, +so the ``CONDA_PREFIX`` environment variable +should be used to specify the install path. + +Linux and macOS +............... + +On Linux and macOS, +use these commands to build and install the *heat* model: + +.. code:: bash + + $ cd bmi-example-c + $ mkdir _build && cd _build + $ cmake .. -DCMAKE_INSTALL_PREFIX=$CONDA_PREFIX + $ make + $ make install + +Verify the install by testing for the existence of the header +of the library containing the compiled *heat* model: + +.. code:: bash + + $ test -f $CONDA_PREFIX/include/bmi_heat.h + +Windows +....... + +Building on Windows requires +Microsoft Visual Studio 2017 or Microsoft Build Tools for Visual Studio 2017. +To build and install the *heat* model, +the following commands must be run in a `Developer Command Prompt`_: + +.. code:: + + > cd bmi-example-c + > mkdir _build && cd _build + > cmake .. ^ + -G "NMake Makefiles" ^ + -DCMAKE_INSTALL_PREFIX=%CONDA_PREFIX% ^ + -DCMAKE_BUILD_TYPE=Release + > cmake --build . --target install --config Release + +Verify the install by testing for the existence of the header +of the library containing the compiled *heat* model: + +.. code:: + + > if not exist %LIBRARY_INC%\\bmi_heat.h exit 1 + +Create the *babelizer* input file +--------------------------------- + +The *babelizer* input file provides information to the *babelizer* +about the model to be wrapped. +The input file is created with the ``babelize generate`` subcommand. + +Return to our initial ``build`` directory and call ``babelize generate`` with: + +.. code:: bash + + $ cd ~/build + $ babelize generate babel_heatc.toml \ + --language=c \ + --summary="PyMT plugin for heat model" \ + --entry-point=HeatModel=bmiheatc:register_bmi_heat \ + --name=heatc \ + --requirement="" + +In this call, +the *babelizer* will also interactively prompt for author name, author email, +GitHub username, and license. +These can be optionally be filled in, or the defaults can be used. + +The resulting file, ``babel_heatc.toml``, +will look something like this: + +.. include:: babel_heatc.toml + :literal: + +For more information on the entries and sections of the *babelizer* input file, +see `Input file <./readme.html#input-file>`_. + + +Wrap the model with the *babelizer* +----------------------------------- + +Generate Python bindings for the model with the ``babelize init`` subcommand: + +.. code:: bash + + $ babelize init babel_heatc.toml . + +The results are placed in a new directory, ``pymt_heatc``, +under the current directory. + +.. code:: bash + + $ ls -aF pymt_heatc + ./ MANIFEST.in recipe/ + ../ Makefile requirements-library.txt + .git/ README.rst requirements-testing.txt + .gitignore babel.toml requirements.txt + .travis.yml docs/ setup.cfg + CHANGES.rst meta/ setup.py + CREDITS.rst pymt_heatc/ + LICENSE pyproject.toml + +Before we can build the Python bindings, +we must ensure that the dependencies required by the toolchain, +as well as any required by the model, +as specified in the *babelizer* input file (none in this case), +are satisfied. + +Change to the ``pymt_heatc`` directory and install dependencies +into the conda environment: + +.. code:: bash + + $ cd pymt_heatc + $ conda install -c conda-forge --file=requirements.txt --file=requirements-library.txt + + +Now build the Python bindings with: + +.. code:: bash + + $ make install + +This command sets off a long list of messages, +at the end of which you'll hopefully see: + +.. code:: bash + + Successfully installed pymt-heatc + +Pause a moment to see what we've done. +Start a Python session and try the following commands: + +.. code:: python + + >>> from pymt_heatc import HeatModel + >>> m = HeatModel() + >>> print(m.get_component_name()) + The 2D Heat Equation + +We've imported the *heat* model, +written in C, +into Python! + +At this point, +it's a good idea to run the *bmi-tester* (`GitHub repo `_) +over the model. +The *bmi-tester* exercises each BMI method exposed through Python, +ensuring it works correctly. + +Install *bmi-tester* with: + +.. code:: bash + + $ conda install -c conda-forge bmi-tester + +Before running the *bmi-tester*, one last piece is needed. +Like all models equipped with a BMI, +*heat* uses a :term:`configuration file` to specify initial parameter values. +Download the file :download:`config.txt ` for use here. + +Run the *bmi-tester* with: + +.. code:: bash + + $ bmi-test pymt_heatc:HeatModel --config-file=config.txt --root-dir=. -vvv + +This command sets off a long list of messages, +ending with + +.. code:: bash + + 🎉 All tests passed! + +if everything has been built correctly. + + +Add metadata to make a *pymt* component +--------------------------------------- + +The final step in wrapping the *heat* model +is to add metadata used by the `Python Modeling Tool`_, *pymt*. +CSDMS develops a set of standards, +the `CSDMS Model Metadata`_, +that provides a detailed and formalized description of a model. +The metadata allow *heat* to be run and and :term:`coupled ` +with other models that expose a BMI and have been similarly wrapped +with the *babelizer*. + +Recall the *babelizer* outputs the wrapped *heat* model +to the directory ``pymt_heatc``. +Under this directory, +the *babelizer* created a directory for *heat* model metadata, +``meta/HeatModel``. +The contents of this directory are currently: + +.. code:: bash + + $ ls meta/HeatModel/ + api.yaml + +The file ``api.yaml`` is automatically generated by the *babelizer*. +To complete the description of the *heat* model, +other metadata files are needed, including: + +* :download:`info.yaml ` +* :download:`run.yaml ` +* a :download:`templated model configuration file ` +* :download:`parameters.yaml ` + +`Descriptions`_ of these files and their roles +are given in the CSDMS Model Metadata repository. +Download each of the files using the links in the list above +and place them in the ``pymt_heatc/meta/HeatModel`` directory +alongside ``api.yaml``. + +Next, install *pymt*: + +.. code:: bash + + $ conda install -c conda-forge pymt + +Then start a Python session and show that the *heat* model +can be called through *pymt*: + +.. code:: python + + >>> from pymt.models import HeatModel + >>> m = HeatModel() + >>> print(m.name) + The 2D Heat Equation + +A longer example, +:download:`pymt_heatc_ex.py `, +is included in the documentation. +For easy viewing, it's reproduced here verbatim: + +.. include:: examples/pymt_heatc_ex.py + :literal: + +:download:`Download ` this Python script, +then run it with: + +.. code:: bash + + $ python pymt_heatc_ex.py + + +Summary +------- + +Using the *babelizer*, we wrapped the *heat* model, which is written in C. +It can now be called as a *pymt* component in Python. + +The steps for wrapping a model with the *babelizer* outlined in this example +can also be applied to models written in C++ and Fortran. + + +.. + Links + +.. _bmi-example-c: https://github.com/csdms/bmi-example-c +.. _instructions: https://github.com/csdms/bmi-example-c/blob/master/README.md +.. _Developer Command Prompt: https://docs.microsoft.com/en-us/dotnet/framework/tools/developer-command-prompt-for-vs +.. _bmi-tester: https://github.com/csdms/bmi-tester +.. _Python Modeling Tool: https://pymt.readthedocs.io +.. _CSDMS Model Metadata: https://github.com/csdms/model_metadata +.. _Descriptions: https://github.com/csdms/model_metadata/blob/develop/README.rst diff --git a/docs/source/examples/config.txt b/docs/source/examples/config.txt new file mode 100644 index 00000000..fe07de38 --- /dev/null +++ b/docs/source/examples/config.txt @@ -0,0 +1 @@ +1.5, 8.0, 6, 5 diff --git a/docs/source/examples/heat.txt b/docs/source/examples/heat.txt new file mode 100644 index 00000000..7ea565ae --- /dev/null +++ b/docs/source/examples/heat.txt @@ -0,0 +1 @@ +{{thermal_diffusivity}}, {{run_duration}}, {{number_of_columns}}, {{number_of_rows}} diff --git a/docs/source/examples/heatc_ex.py b/docs/source/examples/heatc_ex.py new file mode 100644 index 00000000..08d12a30 --- /dev/null +++ b/docs/source/examples/heatc_ex.py @@ -0,0 +1,67 @@ +"""An example of running the heatc model through its BMI.""" +import numpy as np +from pymt_heatc import HeatModel + + +config_file = 'config.txt' +np.set_printoptions(formatter={'float': '{: 6.1f}'.format}) + + +# Instatiate an initialize the model. +m = HeatModel() +print(m.get_component_name()) +m.initialize(config_file) + +# List the model's exchange items. +print('Input vars:', m.get_input_var_names()) +print('Output vars:', m.get_output_var_names()) + +# Get the grid_id for the plate_surface__temperature variable. +var_name = 'plate_surface__temperature' +print('Variable {}'.format(var_name)) +grid_id = m.get_var_grid(var_name) +print(' - grid id:', grid_id) + +# Get grid and variable info for plate_surface__temperature. +print(' - grid type:', m.get_grid_type(grid_id)) +grid_rank = m.get_grid_rank(grid_id) +print(' - rank:', grid_rank) +grid_shape = np.empty(grid_rank, dtype=np.int32) +m.get_grid_shape(grid_id, grid_shape) +print(' - shape:', grid_shape) +grid_size = m.get_grid_size(grid_id) +print(' - size:', grid_size) +grid_spacing = np.empty(grid_rank, dtype=np.float) +m.get_grid_spacing(grid_id, grid_spacing) +print(' - spacing:', grid_spacing) +grid_origin = np.empty(grid_rank, dtype=np.float) +m.get_grid_origin(grid_id, grid_origin) +print(' - origin:', grid_origin) +print(' - variable type:', m.get_var_type(var_name)) +print(' - units:', m.get_var_units(var_name)) +print(' - itemsize:', m.get_var_itemsize(var_name)) +print(' - nbytes:', m.get_var_nbytes(var_name)) + +# Get the initial temperature values. +val = np.empty(grid_shape, dtype=np.float) +m.get_value(var_name, val) +print(' - initial values (gridded):') +print(val.reshape(np.roll(grid_shape, 1))) + +# Get time information from the model. +print('Start time:', m.get_start_time()) +print('End time:', m.get_end_time()) +print('Current time:', m.get_current_time()) +print('Time step:', m.get_time_step()) +print('Time units:', m.get_time_units()) + +# Advance the model by one time step. +m.update() +print('Updated time:', m.get_current_time()) + +# Advance the model until a later time. +m.update_until(5.0) +print('Later time:', m.get_current_time()) + +# Finalize the model. +m.finalize() diff --git a/docs/source/examples/info.yaml b/docs/source/examples/info.yaml new file mode 100644 index 00000000..a1397b57 --- /dev/null +++ b/docs/source/examples/info.yaml @@ -0,0 +1,8 @@ +summary: | + This model solves the two-dimensional heat equation on a uniform + rectilinear grid. +url: https://github.com/csdms/bmi-example-c +author: CSDMS +email: csdms@colorado.edu +version: 0.2 +license: MIT diff --git a/docs/source/examples/parameters.yaml b/docs/source/examples/parameters.yaml new file mode 100644 index 00000000..3c1c5f2b --- /dev/null +++ b/docs/source/examples/parameters.yaml @@ -0,0 +1,42 @@ +run_duration: + description: Simulation run time + value: + type: float + default: 1.0 + units: s + range: + min: 0.0 + max: 1000000.0 + +thermal_diffusivity: + name: Thermal diffusivity + description: Thermal diffusivity + value: + type: float + default: 1.0 + range: + min: 0.0 + max: 10.0 + units: 'm2 s-1' + +number_of_rows: + name: Number of rows + description: Number of grid rows + value: + type: int + default: 10 + range: + min: 0 + max: 10000 + units: '1' + +number_of_columns: + name: Number of columns + description: Number of grid columns + value: + type: int + default: 10 + range: + min: 0 + max: 10000 + units: '1' diff --git a/docs/source/examples/pymt_heatc_ex.py b/docs/source/examples/pymt_heatc_ex.py new file mode 100644 index 00000000..e4bc023b --- /dev/null +++ b/docs/source/examples/pymt_heatc_ex.py @@ -0,0 +1,62 @@ +"""Run the heat model in pymt.""" +import numpy as np +from pymt.models import HeatModel + + +# Instantiate the component and get its name. +m = HeatModel() +print(m.name) + +# Call setup, then initialize the model. +args = m.setup('.') +m.initialize(*args) + +# List the model's exchange items. +print('Number of input vars:', len(m.input_var_names)) +for var in m.input_var_names: + print(' - {}'.format(var)) +print('Number of output vars:', len(m.output_var_names)) +for var in m.output_var_names: + print(' - {}'.format(var)) + +# Get variable info. +var_name = m.output_var_names[0] +print('Variable {}'.format(var_name)) +print(' - variable type:', m.var_type(var_name)) +print(' - units:', m.var_units(var_name)) +print(' - itemsize:', m.var_itemsize(var_name)) +print(' - nbytes:', m.var_nbytes(var_name)) +print(' - location:', m.var_location(var_name)) + +# Get grid info for variable. +grid_id = m.var_grid(var_name) +print(' - grid id:', grid_id) +print(' - grid type:', m.grid_type(grid_id)) +print(' - rank:', m.grid_ndim(grid_id)) +print(' - size:', m.grid_node_count(grid_id)) +print(' - shape:', m.grid_shape(grid_id)) + +# Get time information from the model. +print('Start time:', m.start_time) +print('End time:', m.end_time) +print('Current time:', m.time) +print('Time step:', m.time_step) +print('Time units:', m.time_units) + +# Get the initial values of the variable. +print('Get values of {}...'.format(var_name)) +val = m.var[var_name].data +print(' - values at time {}:'.format(m.time)) +print(val) + +# Advance the model by one time step. +m.update() +print('Update: current time:', m.time) + +# Advance the model until a later time. +m.update_until(5.0) +print('Update: current time:', m.time) + +# Finalize the model. +m.finalize() +print('Done.') diff --git a/docs/source/examples/run.yaml b/docs/source/examples/run.yaml new file mode 100644 index 00000000..3b0457ff --- /dev/null +++ b/docs/source/examples/run.yaml @@ -0,0 +1 @@ +config_file: heat.txt diff --git a/docs/source/glossary.rst b/docs/source/glossary.rst new file mode 100644 index 00000000..f2f15391 --- /dev/null +++ b/docs/source/glossary.rst @@ -0,0 +1,118 @@ +Glossary +======== + +A glossary of terms used with the *babelizer*. + + +.. glossary:: + + $ + + The default shell prompt. + + Anaconda + + A Python distribution that includes libraries for scientific + computing and a package manager. See + https://www.anaconda.com/distribution for more information. + + Basic Model Interface + + A set a functions that are used to interact with and control a + model. See https://bmi.readthedocs.io for more information. + + BMI + + See :term:`Basic Model Interface`. + + Community Surface Dynamics Modeling System + + CSDMS is an NSF-funded program that seeks to transform the + science and practice of earth-surface dynamics modeling. For + more information, visit https://csdms.colorado.edu. + + class + + A program that acts as a template for creating + :term:`objects`. + + conda + + The package manager for :term:`Anaconda`. Also an informal name + for an Anaconda installation. + + conda-forge + + A collection of community-built packages distributed by + :term:`Anaconda`. See https://conda-forge.org. + + conda environment + + A :term:`conda` sub-installation that isolates a group of + packages from the main conda installation. + + configuration file + + A file, usually in a text-based format, that lists the tunable + parameters of a :term:`model` and supplies their initial values. + + coupling + + See :term:`model coupling`. + + CSDMS + + See :term:`Community Surface Dynamics Modeling System`. + + data + + Information held by an :term:`object`. + + git + + Version control software. + + import + + The process of bringing code from a Python :term:`module` into + another module or into an interactive Python session. + + instance + + See :term:`object`. + + method + + Programs that act upon the :term:`data` of an :term:`object`. + + model + + A computer program that attempts to describe a physical process + with mathematical relationships that evolve over time and are + solved numerically. For more information, see, for example, + https://en.wikipedia.org/wiki/Numerical_modeling_(geology). + + model coupling + + Models are *coupled* when they exchange inputs and outputs, + often at the resolution of individual time steps. *One-way + coupling* occurs when the outputs from one model are used as + inputs to another model. *Two-way coupling* is when outputs from + one model are used as inputs for another model, which in turn + supplies its outputs to the first model as inputs, producing a + feedback. + + module + + A file (with the ``.py`` extension) that contains Python code. + + NumPy + + A Python library that provides arrays. Outputs from *pymt* are + NumPy arrays. See also http://www.numpy.org. + + object + + A variable that is a concrete example of a + :term:`class`. Objects have :term:`data` and + :term:`methods` that act upon those data. diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100644 index 00000000..fe96c93c --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,82 @@ +.. image:: _static/babelizer-logo-lowercase.png + :align: center + :scale: 85% + :alt: babelizer + :target: https://babelizer.readthedocs.io/ + +.. title:: babelizer + + +The *babelizer* is an open source Python utility, +developed by the `Community Surface Dynamics Modeling System`_ (CSDMS), +for wrapping models that expose a `Basic Model Interface`_ (BMI) +so they can be imported as Python packages. + +Supported languages include: + +* C +* C++ +* Fortran + +Within Python, these models, regardless of their core language, +appear as classes that expose a BMI. +Users are then able to run models interactively +through the Python command line or Jupyter Notebook, +and programmatically through Python scripts; +they can also use Python-based BMI tools such as +the `bmi-tester`_, `pymt`_, and `Landlab`_. + + +User Guide +========== + +.. toctree:: + :maxdepth: 2 + + readme + example + glossary + + +API Reference +------------- + +If you are looking for information on a specific function, class, or +method, this part of the documentation is for you. + +.. toctree:: + :maxdepth: 2 + + api/index + + +Help +==== + +Adding a BMI to a model and babelizing it can be a daunting task. +If you'd like assistance, CSDMS can help. +Depending on your need, we can provide advice or consulting services. +Feel free to contact us through the `CSDMS Help Desk`_. + + +Miscellaneous Pages +------------------- + +.. toctree:: + :maxdepth: 2 + + authors + changelog + contributing + license + + +.. + Links + +.. _Community Surface Dynamics Modeling System: https://csdms.colorado.edu +.. _Basic Model Interface: https://github.com/csdms/bmi +.. _bmi-tester: https://github.com/csdms/bmi-tester +.. _pymt: https://pymt.readthedocs.io/ +.. _Landlab: https://landlab.github.io/ +.. _CSDMS Help Desk: https://github.com/csdms/help-desk diff --git a/docs/source/license.rst b/docs/source/license.rst new file mode 100644 index 00000000..b187ecc8 --- /dev/null +++ b/docs/source/license.rst @@ -0,0 +1,5 @@ +======= +License +======= + +.. include:: ../../LICENSE.rst diff --git a/docs/source/readme.rst b/docs/source/readme.rst new file mode 100644 index 00000000..a6210d3d --- /dev/null +++ b/docs/source/readme.rst @@ -0,0 +1 @@ +.. include:: ../../README.rst diff --git a/requirements-docs.txt b/requirements-docs.txt new file mode 100644 index 00000000..2236f915 --- /dev/null +++ b/requirements-docs.txt @@ -0,0 +1 @@ +sphinx>=1.5.1, <3 diff --git a/setup.py b/setup.py index 35afc529..55343e9d 100644 --- a/setup.py +++ b/setup.py @@ -35,5 +35,5 @@ def read(filename): install_requires=open("requirements.txt", "r").read().splitlines(), packages=find_packages(), include_package_data=True, - entry_points={"console_scripts": ["babelize=babelizer.cli:babelize",],}, + entry_points={"console_scripts": ["babelize=babelizer.cli:babelize"]}, ) diff --git a/tests/test_c.py b/tests/test_c.py index de8ade26..20374fa2 100644 --- a/tests/test_c.py +++ b/tests/test_c.py @@ -3,9 +3,7 @@ import pathlib import shutil import subprocess -import sys -import pytest from click.testing import CliRunner from babelizer.cli import babelize