├── .github └── workflows │ ├── docs.yml │ └── python-publish.yml ├── .gitignore ├── README.md ├── docs ├── Makefile ├── make.bat ├── requirements.txt └── source │ ├── conf.py │ └── index.rst ├── examples ├── README.txt ├── ZombieApocalypse.ipynb ├── hysplit_traj │ ├── plot_hysplit.py │ └── trajdump.txt ├── knote_ae_2015 │ ├── README.txt │ ├── SummerClean.ipynb │ ├── chemical_nox.pdf │ ├── chemical_ozone.pdf │ ├── mean_emis.csv │ ├── physical.pdf │ ├── plot_knoteae2015.py │ ├── summercb05_ref.pykpp.tsv │ └── summerenv.tsv ├── plot_acp2006.py ├── plot_henderson2011.py └── plot_zombies.py ├── pykpp ├── __init__.py ├── __main__.py ├── funcs │ ├── __init__.py │ ├── am3.py │ ├── camx.py │ ├── chimere.py │ ├── cmaq.py │ ├── geoschem.py │ ├── geoschemkpp.py │ ├── kpp.py │ ├── mcm.py │ ├── mozart4.py │ └── racm.py ├── main.py ├── mech.py ├── models │ ├── MCM_J.def │ ├── UFAuxMech09.eqn │ ├── atoms │ ├── cb05cl.eqn │ ├── cb6r5m_ae7_aq.eqn │ ├── cbm4.eqn │ ├── cbm4.kpp │ ├── chimere1.def │ ├── chimere2.def │ ├── geoschem_standard_v11.eqn │ ├── geoschem_v11.def │ ├── geossuper_chem.eqn │ ├── geossuper_chem.kpp │ ├── mcm_ch4.kpp │ ├── melchior1.eqn │ ├── melchior1_soa_cpx.eqn │ ├── melchior2.eqn │ ├── melchior2_soa_cpx.eqn │ ├── prep │ │ ├── __init__.py │ │ ├── am32kpp.py │ │ ├── chimere2kpp.py │ │ ├── cmaq2chimere.py │ │ ├── cmaq2kpp.py │ │ ├── geoschem2kpp.py │ │ └── sbox2kpp.py │ ├── saprc07cl.eqn │ ├── simple_organic.eqn │ ├── small_strato.eqn │ └── small_strato.kpp ├── morpho │ ├── __init__.py │ └── jtable.py ├── parse.py ├── plot.py ├── stdfuncs.py ├── tests │ ├── __init__.py │ ├── __main__.py │ ├── test_simple.py │ └── test_updaters.py ├── tuv │ ├── __init__.py │ ├── tuv4pt1.py │ └── tuv5pt0.py └── updaters.py ├── scripts └── pykpprun.py ├── setup.py └── tox.ini /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: Docs 2 | on: [workflow_dispatch] 3 | permissions: 4 | contents: write 5 | jobs: 6 | docs: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v3 10 | - uses: actions/setup-python@v3 11 | - name: Install dependencies 12 | run: | 13 | pip install -r docs/requirements.txt 14 | - name: Install pykpp 15 | run: | 16 | pip install -e . --no-deps --force-reinstall 17 | - name: Sphinx build 18 | run: | 19 | cd docs 20 | make html 21 | - name: Deploy 22 | uses: peaceiris/actions-gh-pages@v3 23 | if: ${{ github.ref == 'refs/heads/main' }} 24 | with: 25 | publish_branch: gh-pages 26 | github_token: ${{ secrets.GITHUB_TOKEN }} 27 | publish_dir: docs/build/html 28 | force_orphan: true 29 | -------------------------------------------------------------------------------- /.github/workflows/python-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will upload a Python Package using Twine when a release is created 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries 3 | 4 | # This workflow uses actions that are not certified by GitHub. 5 | # They are provided by a third-party and are governed by 6 | # separate terms of service, privacy policy, and support 7 | # documentation. 8 | 9 | name: Upload Python Package 10 | 11 | on: 12 | release: 13 | types: [published] 14 | 15 | permissions: 16 | contents: read 17 | 18 | jobs: 19 | deploy: 20 | 21 | runs-on: ubuntu-latest 22 | 23 | steps: 24 | - uses: actions/checkout@v3 25 | - name: Set up Python 26 | uses: actions/setup-python@v3 27 | with: 28 | python-version: '3.x' 29 | - name: Install dependencies 30 | run: | 31 | python -m pip install --upgrade pip 32 | pip install build 33 | - name: Build package 34 | run: python -m build 35 | - name: Publish package 36 | uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29 37 | with: 38 | user: __token__ 39 | password: ${{ secrets.PYPI_API_TOKEN }} 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | build/* 3 | dist/* 4 | .DS_Store 5 | *.dat 6 | *.ncf 7 | # *.txt 8 | src/pykpp/test/*/ 9 | *.tsv 10 | *.csv 11 | *.png 12 | docs/build/ 13 | docs/source/api/ 14 | docs/source/auto_examples/ 15 | MANIFEST 16 | MANIFEST.in 17 | pykpp.egg-info 18 | .ipynb_checkpoints 19 | .tox 20 | .coverage 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | pykpp 2 | ===== 3 | 4 | [![Docs](https://github.com/barronh/pykpp/actions/workflows/docs.yml/badge.svg)](https://barronh.github.io/pykpp/) 5 | 6 | pykpp is a KPP-like chemical mechanism parser that produces a box model solvable by SciPy's odeint solver 7 | 8 | A good starting place is the [documentation with examples](https://barronh.github.io/pykpp/). 9 | 10 | Run Examples No installation 11 | ---------------------------- 12 | * Requires a google account. 13 | * Uses Google's Colab 14 | * Click Badge [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/barronh/pykpp/) 15 | 16 | 17 | 18 | Running Instructions 19 | -------------------- 20 | 21 | 1. Open DOS Prompt on Windows or Terminal on Linux/Mac OSX 22 | 2. Type "python -m pykpp model.kpp" where model.kpp is a valid KPP input file. 23 | 3. Example below assumes Installation and that mcm_ch4.kpp has been downloaded to the working folder 24 | 25 | ``` 26 | $ python -m pykpp mcm_ch4.kpp --solver="odeint,rtol=1e-5,atol=[1e-5 if s != 'O1D' else 1 for s in out.allspcs]" 27 | Found PYTHON! 28 | Species: 30 29 | Reactions: 68 30 | /Users/barronh/Development/pykpp/src/pykpp/updaters.py:7: UserWarning: Using RO2 = 1e-32 @ 18000.0 31 | def __init__(self, *args, **kwds): 32 | 0.0%: {t:18000,O3:60,NO2:4.1E-43,RO2/CFACTOR:4.1E-43} 33 | 1.0%: {t:18301,O3:59,NO2:0.98,RO2/CFACTOR:4.1E-43} 34 | 2.1%: {t:18606,O3:59,NO2:0.98,RO2/CFACTOR:7.7E-05} 35 | ... 36 | 98.8%: {t:46464,O3:58,NO2:0.041,RO2/CFACTOR:0.046} 37 | 100.1%: {t:46837,O3:57,NO2:0.039,RO2/CFACTOR:0.047} 38 | Solved in 0.410105 seconds 39 | ``` 40 | 41 | * Example input files can be downloaded from https://github.com/barronh/pykpp/tree/main/pykpp/models and are also in the models folder of pykpp source 42 | 43 | Install Instructions 44 | -------------------- 45 | 46 | Instructions 47 | ------------ 48 | 49 | 1. Install Anaconda (https://www.continuum.io/downloads) 50 | 2. Download release version zip file from github. (or source) 51 | 3. Extract zip file 52 | 4. navigate to extracted folder pykpp-X.Y (the unzipped folder from pykpp-X.Y.zip) 53 | 5. Install pykpp from DOS terminal or terminal on Mac/Linux 54 | 55 | DOS: `python.exe setup.py install` 56 | Terminal: `python setup.py install` 57 | 58 | 6. Run `simple_organic` test case 59 | 1. Copy `simple_organic.kpp` and `ekma.py` from test folder (either through github or from source 60 | 2. navigate to where you downloaded `simple_organic.kpp` and `ekma.py` 61 | 7. Run pykpp 62 | 63 | DOS: `python.exe -m pykpp simple_organic.kpp -c ekma.py` 64 | Terminal: `python -m pykpp simple_organic.kpp -c ekma.py` 65 | 66 | 8. Compare ekma.png, which is an EKMA diagram, to `Figure 6.12 from Seinfeld & Pandis ACP 2nd ed. 2006` (search Google Books for that phrase to compare). 67 | 9. Do something more complicated (see Using pykpp with MCM) 68 | 69 | 70 | 71 | Using pykpp with a MCM Extraction 72 | --------------------------------- 73 | 74 | Adapt mcm kpp output for pykpp following these steps. 75 | 76 | 1. Fix line endings (mixed linux/windows) 77 | 2. Remove header comment (First 16 lines) 78 | 3. Remove everything above `#INCLUDE F90_RCONST` 79 | 4. replace `F90_RCONST` with `PY_UTIL` 80 | 5. replace `USE constants` with 2 lines 81 | 82 | `add_world_updater(func_updater(Update_M, incr = 300))` 83 | `add_world_updater(func_updater(Update_THETA, incr = 300))` 84 | 85 | 6. remove comment lines (preceded by `!`) 86 | 7. replace `RO2 = & C(ind_XXX1) + C(ind_XXX2) + ....` with `RO2 = y[[ind_XXX1, ind_XXX2, ...]].sum()` 87 | 8. remove `CALL mcm_constants(time, temp, M, N2, O2, RO2, H2O) ` line 88 | 9. replace all `D-` and `D+` wit `e-` and `e+` (also check for `#D#`, which is an implicit +, and replace as necessary) 89 | 10. wrap code in `add_world_updater(code_updater("""""", incr = 300))` 90 | 91 | For example, methane chemistry changes from 92 | 93 | ``` 94 | {********************************************************************* ; 95 | * A citation to the MCM website and the relevant mechanism * ; 96 | * construction protocols should be given in any publication using * ; 97 | * information obtained from this source, using the following or * ; 98 | * comparable wording: * ; 99 | * The chemical mechanistic information was taken from the Master * ; 100 | * Chemical Mechanism, MCM v3.3.1 (ref), via website: * ; 101 | * http://mcm.leeds.ac.uk/MCM. * ; 102 | * The reference should be: (Jenkin et al., Atmos. Environ., 31, 81, * ; 103 | * 1997; Saunders et al., Atmos. Chem. Phys., 3, 161, 2003), for * ; 104 | * non aromatic schemes; (Jenkin et al., Atmos. Chem. Phys., 3, * ; 105 | * 181, 2003; Bloss et al., Atmos. Chem. Phys., 5, 641, 2005), for * ; 106 | * aromatic schemes; (Jenkin et al., Atmos. Chem. Phys., 12, * ; 107 | * 5275, 2012), for the beta-caryophyllene scheme and (Jenkin et al., ; 108 | * Atmos. Chem. Phys., 15, 11433, 2015), for the isoprene scheme. * ; 109 | ********************************************************************* ;} 110 | #INLINE F90_GLOBAL 111 | REAL(dp)::M, N2, O2, RO2, H2O 112 | #ENDINLINE {above lines go into MODULE KPP_ROOT_Global} 113 | #INCLUDE atoms 114 | #DEFVAR 115 | HCHO = IGNORE ; 116 | ... 117 | #INLINE F90_RCONST 118 | USE constants 119 | !end of USE statements 120 | ! 121 | ! start of executable statements 122 | RO2 = & 123 | C(ind_CH3O2) 124 | KRO2NO = 2.7D-12*EXP(360/TEMP) 125 | KRO2HO2 = 2.91D-13*EXP(1300/TEMP) 126 | ... 127 | #ENDINLINE 128 | ``` 129 | 130 | and become 131 | 132 | ``` 133 | #INLINE PY_UTIL 134 | add_world_updater(func_updater(Update_M, incr = 300)) 135 | add_world_updater(func_updater(Update_THETA, incr = 300)) 136 | add_world_updater(code_updater(""" 137 | EXP = exp 138 | LOG10 = log10 139 | try: 140 | RO2 = y[[ind_CH3O2]].sum() 141 | except: 142 | warn('Using RO2 = 1e-32 @ %s' % t) 143 | RO2 = 1e-32 144 | 145 | KRO2NO = 2.7e-12*EXP(360/TEMP) 146 | KRO2HO2 = 2.91e-13*EXP(1300/TEMP) 147 | KBPPN = (KPPN0*KPPNI)*FCPPN/(KPPN0+KPPNI) 148 | """, incr = 300, message = 'MCM') 149 | ``` 150 | 151 | 11. Replace `= :` with `= DUMMY :` in equations 152 | 12. Replace `J(##)` with `MCMJ(##, THETA)` 153 | 13. Move any species you want as constants from reactants to rate constant and comment them out as products. 154 | 14. Add intialization code. For example, 155 | 156 | ``` 157 | #INLINE PY_INIT 158 | DT = 300 159 | TSTART = 5 * 3600. 160 | TEND = TSTART + 8 * 3600 161 | P = 101325. 162 | TEMP = 298. 163 | H2O = h2o_from_rh_and_temp(80., TEMP) 164 | StartDate = 'datetime(2013, 6, 1)' 165 | Latitude_Degrees = 35. 166 | Longitude_Degrees = 0.00E+00 167 | #ENDINLINE 168 | 169 | #INITVALUES 170 | CFACTOR = P * Avogadro / R / TEMP * centi **3 * nano {ppb-to-molecules/cm3} 171 | ALL_SPEC=1e-32; 172 | CH4 = 1850 173 | NO = 1. 174 | NO2 = 7. 175 | O3 = 60. 176 | ``` 177 | 178 | The mcm_ch4.kpp file under pykpp/models was extracted by selecting CH4 from the MCM website, and exporting KPP format with inorganic reactions and rate constants on 2016-01-28 at 10:00am and converted following the steps described above. It can be used as a reference. 179 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = source 9 | BUILDDIR = build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | clean: 18 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 19 | rm -rf build source/api source/auto_examples 20 | 21 | # Catch-all target: route all unknown targets to Sphinx using the new 22 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 23 | %: Makefile api 24 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 25 | 26 | api: 27 | sphinx-apidoc -f --maxdepth=1 -o ./source/api ../pykpp 28 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=source 11 | set BUILDDIR=build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.https://www.sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | ipython 2 | notebook 3 | requests 4 | numpy 5 | scipy 6 | matplotlib 7 | pandas 8 | sphinx==7.0.1 9 | sphinx-rtd-theme 10 | sphinx-gallery<=0.13 11 | sphinx_design 12 | sphinx-copybutton 13 | pydata-sphinx-theme<0.9.0 14 | myst_nb 15 | -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | # 13 | import os 14 | import sys 15 | from sphinx_gallery.sorting import ExplicitOrder 16 | # from sphinx_gallery.sorting import ExampleTitleSortKey 17 | from sphinx_gallery.sorting import FileNameSortKey 18 | pykpproot = os.path.abspath('../../') 19 | sys.path.insert(0, pykpproot) 20 | 21 | 22 | with open('../../pykpp/__init__.py', 'r') as initf: 23 | for _l in initf.readlines(): 24 | if _l.startswith('__version__ = '): 25 | release = _l.split(' = ')[-1][1:-1] 26 | break 27 | else: 28 | release = '0.0.0' 29 | 30 | # -- Project information ----------------------------------------------------- 31 | 32 | project = 'pykpp' 33 | copyright = '2023, Barron H. Henderson' 34 | author = 'Barron H. Henderson' 35 | 36 | 37 | # -- General configuration --------------------------------------------------- 38 | 39 | # Add any Sphinx extension module names here, as strings. They can be 40 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 41 | # ones. 42 | extensions = [ 43 | 'sphinx.ext.autodoc', 44 | 'sphinx.ext.autosummary', 45 | 'sphinx.ext.githubpages', 46 | 'sphinx.ext.intersphinx', 47 | 'sphinx.ext.mathjax', 48 | 'sphinx.ext.viewcode', 49 | 'IPython.sphinxext.ipython_directive', 50 | 'IPython.sphinxext.ipython_console_highlighting', 51 | 'matplotlib.sphinxext.plot_directive', 52 | 'sphinx_copybutton', 53 | 'sphinx_design', 54 | #'sphinx_rtd_theme', 55 | 'myst_nb', 56 | 'sphinx_gallery.gen_gallery', 57 | 'sphinx.ext.napoleon', 58 | ] 59 | 60 | sphinx_gallery_conf = { 61 | 'examples_dirs': '../../examples', 62 | 'gallery_dirs': 'auto_examples', 63 | 'subsection_order': ExplicitOrder([ 64 | '../../examples', 65 | '../../examples/knote_ae_2015', 66 | ]), 67 | 'within_subsection_order': FileNameSortKey, 68 | } 69 | 70 | # Generate the API documentation when building 71 | autoclass_content = 'both' 72 | autosummary_generate = True 73 | autosummary_imported_members = True 74 | 75 | html_sidebars = { 76 | 'userguide': ['searchbox.html', 'sidebar-nav-bs.html'], 77 | 'API': ['searchbox.html', 'sidebar-nav-bs.html'], 78 | 'examples': ['searchbox.html', 'sidebar-nav-bs.html'], 79 | 'notebook-gallery': ['searchbox.html', 'sidebar-nav-bs.html'], 80 | } 81 | 82 | # Add any paths that contain templates here, relative to this directory. 83 | templates_path = ['_templates'] 84 | 85 | # List of patterns, relative to source directory, that match files and 86 | # directories to ignore when looking for source files. 87 | # This pattern also affects html_static_path and html_extra_path. 88 | exclude_patterns = ['_build', '**.ipynb_checkpoints'] 89 | 90 | 91 | # -- Options for HTML output ------------------------------------------------- 92 | 93 | # The theme to use for HTML and HTML Help pages. See the documentation for 94 | # a list of builtin themes. 95 | # 96 | # html_theme = 'sphinx_rtd_theme' 97 | 98 | # Add any paths that contain custom static files (such as style sheets) here, 99 | # relative to this directory. They are copied after the builtin static files, 100 | # so a file named "default.css" will overwrite the builtin "default.css". 101 | html_static_path = ['_static'] 102 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | .. pyrkpp documentation main file, created by 2 | 3 | pykpp User's Guide 4 | =================== 5 | 6 | Python interface similar to the Kinetic Preprocessor (KPP) 7 | 8 | The key value of `pyrkpp` is to make simple simulations easy to run in common 9 | cloud environments like Google Colab, AWS SageMaker, Binder, or even 10 | Jupyter-lite. 11 | 12 | Getting Started 13 | --------------- 14 | 15 | The best way to get started is to install (see below) and then explore the 16 | examples gallery. 17 | 18 | 19 | Installation 20 | ------------ 21 | 22 | You can get the latest version with this command. 23 | 24 | .. code-block:: 25 | 26 | pip install https://github.com/barronh/pykpp/archive/main.zip 27 | 28 | 29 | Issues 30 | ------ 31 | 32 | If you're having any problems, open an issue on github. 33 | 34 | https://github.com/barronh/pykpp/issues 35 | 36 | 37 | Quick Links 38 | ----------- 39 | 40 | * :doc:`auto_examples/index` 41 | * :doc:`api/pykpp` 42 | 43 | .. toctree:: 44 | :maxdepth: 2 45 | :hidden: 46 | :caption: Table of Contents 47 | 48 | self 49 | auto_examples/index 50 | api/pykpp 51 | -------------------------------------------------------------------------------- /examples/README.txt: -------------------------------------------------------------------------------- 1 | pykpp Example Gallery 2 | ===================== 3 | 4 | This gallery houses examples on how to use pykpp to do various 5 | analyses. This includes downloading and visualizing the data. 6 | 7 | Some examples require some features of pykpp that are not required 8 | for minimal functionality. To install all the necessary libaries 9 | for any example, run the command below (use --user if not an admin): 10 | 11 | .. code-block:: bash 12 | 13 | python -m pip install --user https://github.com/barronh/pykpp/archive/main.zip 14 | 15 | In notebooks (e.g., on Google Colab), this can be done live with 16 | the command below (may require kernel restart): 17 | 18 | .. code-block:: python 19 | 20 | %pip install --user https://github.com/barronh/pykpp/archive/main.zip 21 | 22 | To run an example in a notebook: 23 | 24 | 1. Copy the command above into a new cell and run it. 25 | 2. Click on example below to see code. 26 | 3. Copy the code from the example into a new cell and run it. 27 | 28 | -------------------------------------------------------------------------------- /examples/hysplit_traj/plot_hysplit.py: -------------------------------------------------------------------------------- 1 | """ 2 | Advanced Trajectory Simulation 3 | ------------------------------ 4 | 5 | The previous example used a reference set of inputs to create a reasonable 6 | summer day. This can then be adapted to make a specific summer day. This 7 | can be done using a single trajectory or an ensemble of trajectories. 8 | 9 | Prerequisites 10 | ^^^^^^^^^^^^^ 11 | 12 | python -m pip install pyrsig netcdf4 pycno pyproj 13 | 14 | Trajectory Environment 15 | ^^^^^^^^^^^^^^^^^^^^^^ 16 | 17 | You can replace teh "Summer" PBL and temperature using HYSPLIT. HYSPLIT runs 18 | back or forward trajectories and allows you to save out mixing depth and temp. 19 | The output is a text dump (tdump) file that you can eaisly turn into a csv 20 | like the one used here. Similarl 21 | 22 | Trajectory Emissions 23 | ^^^^^^^^^^^^^^^^^^^^ 24 | 25 | The emissions can be replaced with new values several ways. For example, EPA 26 | routinely puts gridded annual emissions on their ftp site. And, the 2022 27 | modeling platform is on AWS. You can sample the emission inventory along the 28 | trajectory environment (lon/lat). For the full modeling platform, you will 29 | have to make some decisions about what emissions to include and how. The 30 | gridded emissions are easy because they can be assumed to be emitted within 31 | the mixed depth. It is more complicated to decide how to treat point emissions. 32 | The files do not include plume rise, nor does it tell you how to treat 33 | horizontal dillution. This tutorial will not answer those questions, because 34 | it will use the annual average reports that are at 12-km resolution with no 35 | vertical resolution. 36 | """ 37 | 38 | # %% 39 | # Part 1: Trajectory Environment Offline 40 | # -------------------------------------- 41 | # 42 | # 1. Go to https://www.ready.noaa.gov/HYSPLIT_traj.php 43 | # 2. Choose "compute archive Trajectories" 44 | # 3. Choose 1 "Starting Locations" in "Normal" mode; next 45 | # 4. Choose HRRR 3km (sigma, U.S., 06/2019-present); next 46 | # 5. Choose the JLG Super Site coordiantes 33.503833 N and 112.095767 W; next 47 | # 6. Choose 20240722_18-23_hrrr; next 48 | # 7. Choose "Backward" 49 | # 8. Choose hour 21Z (peak ozone 86 ppb) 50 | # 9. Enable all "dump meteorology" options (Terrain Height, Ambient Temperature, Mixed Depth,e t) 51 | # 10. Run trajectory and wait 52 | # 11. When complete, download the tdump file save here as tdump.inp 53 | 54 | # %% 55 | # Read Environment 56 | # ^^^^^^^^^^^^^^^^ 57 | # 58 | import pandas as pd 59 | 60 | std = ['tid', 'gid', 'yy', 'mm', 'dd', 'HH', 'MM', 'fhr', 'age', 'lat', 'lon', 'alt'] 61 | trajpath = 'trajdump.txt' 62 | with open(trajpath, 'r') as tf: 63 | for _l in tf: 64 | if 'PRESSURE' in _l: 65 | break 66 | 67 | keys = std + _l.split()[1:] 68 | trajdf = pd.read_csv(tf, names=keys, sep=r'\s+') 69 | 70 | trajdf = trajdf[[k for k in trajdf.columns if k != 'THETA']] 71 | ttime = trajdf.eval('yy * 1e8 + mm * 1e6 + dd * 1e4 + HH * 1e2 + MM') # calc time 72 | trajdf['TSTEP'] = pd.to_datetime(ttime, format='%y%m%d%H%M') # add time coord as TSTEP 73 | trajdf['time_lst'] = trajdf['TSTEP'] + pd.to_timedelta(-112 / 15., unit='h') 74 | trajdf['t'] = (trajdf['time_lst'] - trajdf['time_lst'].min().floor('1d')).dt.total_seconds() 75 | 76 | 77 | # %% 78 | # Download Emissions 79 | # ^^^^^^^^^^^^^^^^^^ 80 | # 81 | 82 | import os 83 | import requests 84 | import pyrsig 85 | 86 | # files are Mg/yr and need to be mole/s... 87 | root = 'https://gaftp.epa.gov/Air/emismod/2022/v1/gridded' 88 | # if you want sectors 89 | # anthpath = '2022hc_cb6_22m_total_nobeis_nofires_withfert_12US1_annual.ncf' 90 | allpath = '2022hc_cb6_22m_total_withbeis_withfires_withfert_12US1_annual.ncf' 91 | 92 | if not os.path.exists(allpath): 93 | with requests.get(f'{root}/{allpath}', verify=False) as r, open(allpath, 'wb') as out: 94 | out.write(r.content) 95 | 96 | ef = pyrsig.open_ioapi(allpath).drop_vars('TFLAG') 97 | 98 | # %% 99 | # Get Emissions Along Path 100 | # ^^^^^^^^^^^^^^^^^^^^^^^^ 101 | import pyproj 102 | import copy 103 | 104 | proj = pyproj.Proj(ef.crs_proj4) 105 | trajdf['COL'], trajdf['ROW'] = proj(trajdf.lon, trajdf.lat) 106 | trajdf['LAY'] = 0 107 | t = trajdf['TSTEP'].to_xarray() # input is time invariant 108 | k = trajdf['LAY'].to_xarray() 109 | r = trajdf['ROW'].to_xarray() 110 | c = trajdf['COL'].to_xarray() 111 | 112 | sopt = dict(TSTEP=t, LAY=k, ROW=r, COL=c, method='nearest') 113 | erdf = ef.sel(**sopt).to_dataframe().drop(ef.dims, axis=1) 114 | 115 | # Convert gases from mole/s to molecules/cm3/s using mixdepth 116 | V = trajdf['MIXDEPTH'] * ef.XCELL * ef.YCELL * 1e6 # cm**3 [=] m * m * m * cm**3 / m**3 117 | # molec/s [=] g/ton * y/d * d/h * h/s * mol/g * molec / mol 118 | unitfactor = 6.022e23 * 907185 / 365 / 24 / 3600 / pd.Series({ 119 | 'NO2': 46.0, 'NO': 30.0, 'HONO': 47.0, 120 | 'SO2': 64.0, 'PACD': 76.0, 'AACD': 60.0, 'ALD2': 44.0, 'FORM': 30.0, 121 | 'MEOH': 32.0, 'FACD': 46.0, 'CO': 28.0, 'ALDX': 58.1, 'GLYD': 60.0, 122 | 'GLY': 58.0, 'MGLY': 72.0, 'ETHA': 30.1, 'ETOH': 46.1, 'KET': 72.1, 123 | 'PAR': 14.0, 'ACET': 58.1, 'PRPA': 44.1, 'ETHY': 26.0, 'ETH': 28.0, 124 | 'OLE': 42.1, 'IOLE': 56.1, 'ISOP': 68.1, 'ISPD': 70.1, 'TERP': 136.2, 125 | 'APIN': 136.2, 'TOL': 92.1, 'XYLMN': 106.2, 'NAPH': 128.2, 'CL2': 71.0, 126 | 'HCL': 36.5, 'SESQ': 204.0, 'BUTADIENE13': 54.0, 'ACROLEIN': 56.1 127 | }) 128 | 129 | edf = erdf[list(unitfactor.index)].multiply(unitfactor).divide(V, axis=0) 130 | 131 | emis = '\n'.join([ 132 | f' EMISSION = {k} : emis_{k} ;' 133 | for k in edf.columns 134 | ]) 135 | 136 | # deposition velocities 137 | vd = dict( 138 | CO=0.03, 139 | SO2=1.8, 140 | NO=0.016, 141 | NO2=0.1, 142 | NO3=0.1, 143 | N2O5=4, 144 | HONO=2.2, 145 | HNO3=4, 146 | HNO4=4, 147 | O3=0.4, 148 | H2O2=0.5, 149 | CH3OOH=0.1, 150 | ) 151 | vdf = pd.DataFrame({ 152 | k: v / (trajdf['MIXDEPTH'] * 100) # convert from cm/s to 1/s 153 | for k, v in vd.items() 154 | }) 155 | 156 | depn = """ 157 | CO = DUMMY : vd_CO ; 158 | SO2 = DUMMY : vd_SO2 ; 159 | NO = DUMMY : vd_NO ; 160 | NO2 = DUMMY : vd_NO2 ; 161 | NO3 = DUMMY : vd_NO3 ; 162 | N2O5 = DUMMY : vd_N2O5 ; 163 | HONO = DUMMY : vd_HONO ; 164 | HNO3 = DUMMY : vd_HNO3 ; 165 | PNA = DUMMY : vd_HNO4 ; 166 | O3 = DUMMY : vd_O3 ; 167 | H2O2 = DUMMY : vd_H2O2 ; 168 | MEPX = DUMMY : vd_CH3OOH ; 169 | """ 170 | 171 | trajedf = trajdf.join( 172 | edf.rename(columns=lambda k: 'emis_' + k) 173 | ).join(vdf.rename(columns=lambda k: 'vd_' + k)) 174 | trajedf['P'] = trajedf['PRESSURE'] * 100 175 | trajedf['TEMP'] = trajedf['AIR_TEMP'] 176 | 177 | 178 | # %% 179 | # Create Mechanism 180 | # 181 | from pykpp.mech import Mech 182 | from pykpp.updaters import Update_M, Update_THETA 183 | from pykpp.stdfuncs import update_func_world 184 | from pykpp.tuv.tuv5pt0 import TUV_J5pt0 185 | import numpy as np 186 | 187 | mechdef = """ 188 | #INLINE PY_INIT 189 | TEMP = 298.15 190 | P = 99600. 191 | t = TSTART = 0 * 3600. 192 | TEND = TSTART + 3600. * 24 193 | DT = 600. 194 | MONITOR_DT = 3600000 195 | StartDate = 'datetime(2024, 7, 22)' 196 | Latitude_Degrees = 33.503833 197 | Longitude_Degrees = -112.095767 198 | #ENDINLINE 199 | 200 | #INITVALUES 201 | CFACTOR = P * Avogadro / R / TEMP * centi **3 * nano {ppb-to-molecules/cm3} 202 | ALL_SPEC=1e-32*CFACTOR; 203 | TOTALNOx=1. 204 | M=1e9 205 | O2=.21*M 206 | N2=.79*M 207 | H2O=0.01*M 208 | CH4=1750 209 | CO=100 210 | O3=60. 211 | NO = 0.1 * TOTALNOx 212 | NO2 = 0.9 * TOTALNOx 213 | SO2 = 1 214 | N2O = 320 215 | {B = 210.; what to do about B?} 216 | 217 | #LOOKAT ALL; 218 | 219 | #include cb6r5m_ae7_aq.eqn 220 | """ + f""" 221 | {depn} 222 | 223 | {emis} 224 | """ 225 | mech = Mech( 226 | mechdef, mechname='test', timeunit='utc', monitor_incr=None, 227 | add_default_funcs=True 228 | ) 229 | 230 | dt = mech.world['DT'] 231 | ts = np.arange(trajedf['t'].min(), trajedf['t'].max(), dt) 232 | envdf = trajedf.set_index('t').to_xarray().interp(t=ts, method='linear').to_dataframe() 233 | rows = [] 234 | olddepth = envdf.iloc[0]['MIXDEPTH'] 235 | fast = ('O', 'O1D', 'HCO3', 'NTR1') 236 | mech.world['atol'] = np.array([10 if k in fast else 1e-3 for k in mech.allspcs]) 237 | mech.world['rtol'] = np.array([0.1 if k in fast else 1e-5 for k in mech.allspcs]) 238 | ybkg = mech.get_y().copy() 239 | for t in ts: 240 | mech.world['t'] = t 241 | mech.world.update(envdf.loc[t].to_dict()) 242 | if olddepth < mech.world["MIXDEPTH"]: 243 | fold = olddepth / mech.world["MIXDEPTH"] 244 | print(fold) 245 | mech.world['O3'] = fold * mech.world['O3'] + (1 - fold) * 60 * mech.world['CFACTOR'] 246 | mech.world['CO'] = fold * mech.world['CO'] + (1 - fold) * 100 * mech.world['CFACTOR'] 247 | olddepth = mech.world["MIXDEPTH"] 248 | mech.run(tstart=t, tend=t + dt) 249 | CFACTOR = mech.world['CFACTOR'] 250 | row = {k: mech.world[k] / CFACTOR for k in mech.allspcs} 251 | for k in ['t', 'CFACTOR', 'TEMP', 'P', 'MIXDEPTH', 'THETA', 'emis_NO', 'SUN_FLUX']: 252 | row[k] = mech.world[k] 253 | row['jNO2'] = TUV_J5pt0('NO2 -> NO + O(3P)', mech.world['THETA']) 254 | rows.append(row) 255 | 256 | # %% 257 | # Plot 258 | # ^^^^ 259 | # 260 | import matplotlib.pyplot as plt 261 | 262 | outdf = pd.DataFrame.from_dict(rows) 263 | 264 | outdf['h_LST'] = outdf['t'] / 3600 265 | noxspcs = ['NO', 'NO2', 'N2O5', 'HONO', 'NO3'] 266 | outdf['NOx'] = sum([outdf[ns] for ns in noxspcs if ns in outdf.columns]) 267 | outdf['TEMP/25 [C]'] = (outdf['TEMP'] - 273.15) / 25. 268 | outdf['OH/10 [ppqv]'] = outdf['OH'] * 1e5 269 | outdf['HO2 [pptv]'] = outdf['HO2'] * 1e3 270 | outdf['PBLH [km]'] = outdf['MIXDEPTH'] * 1e-3 271 | outdf['NO_molps'] = outdf.eval('emis_NO / 6.022e23 * MIXDEPTH * 1e6 ') * ef.XCELL * ef.YCELL 272 | outdf['jNO2*100'] = outdf['jNO2'] * 100 273 | 274 | fig, ax = plt.subplots() 275 | outdf.set_index('h_LST')[['NO_molps', 'jNO2*100', 'PBLH [km]', 'TEMP/25 [C]']].plot(ax=ax) 276 | fig.savefig('hysplit_physical.png') 277 | 278 | fig, ax = plt.subplots() 279 | outdf.set_index('h_LST')[['NOx'] + noxspcs].plot(ax=ax) 280 | ax.set(xlabel='hour [LST]', ylabel='ppb', ylim=(.1, 5), yscale='log') 281 | fig.savefig('hysplit_nox.png') 282 | 283 | fig, ax = plt.subplots() 284 | outdf.set_index('h_LST')[['O3', 'OH/10 [ppqv]', 'HO2 [pptv]']].plot(ax=ax) 285 | ax.set(xlabel='hour (LST)') 286 | fig.savefig('hysplit_ox.png') 287 | 288 | outdf.to_csv('hysplit.csv') -------------------------------------------------------------------------------- /examples/hysplit_traj/trajdump.txt: -------------------------------------------------------------------------------- 1 | 12 1 2 | HRRR 22 7 20 0 1 3 | HRRR 22 7 20 6 1 4 | HRRR 22 7 20 12 1 5 | HRRR 22 7 20 18 1 6 | HRRR 22 7 21 0 1 7 | HRRR 22 7 21 6 1 8 | HRRR 22 7 21 12 1 9 | HRRR 22 7 21 18 1 10 | HRRR 22 7 22 0 1 11 | HRRR 22 7 22 6 1 12 | HRRR 22 7 22 12 1 13 | HRRR 22 7 22 18 1 14 | 1 BACKWARD OMEGA 15 | 22 7 22 21 33.504 -112.096 0.5 16 | 8 PRESSURE THETA AIR_TEMP RAINFALL MIXDEPTH RELHUMID TERR_MSL SUN_FLUX 17 | 1 1 22 7 22 21 0 1 0.0 33.504 -112.096 1099.0 856.2 316.8 303.1 0.0 2198.0 28.8 344.2 930.8 18 | 1 1 22 7 22 20 0 1 -1.0 33.445 -112.179 1153.8 854.4 316.2 302.3 0.0 2145.5 30.5 317.5 982.0 19 | 1 1 22 7 22 19 0 1 -2.0 33.381 -112.253 1282.3 844.9 315.5 300.6 0.0 1923.0 33.7 299.1 979.0 20 | 1 1 22 7 22 18 0 1 -3.0 33.325 -112.327 1288.8 821.6 315.7 298.5 0.0 1372.2 34.5 537.1 909.0 21 | 1 1 22 7 22 17 0 1 -4.0 33.296 -112.446 1460.4 826.4 316.1 299.3 0.0 850.9 26.9 317.2 792.7 22 | 1 1 22 7 22 16 0 1 -5.0 33.256 -112.602 1630.1 812.3 317.1 298.8 0.0 494.8 28.5 292.7 557.5 23 | 1 1 22 7 22 15 0 1 -6.0 33.193 -112.720 1556.6 825.6 316.7 299.8 0.0 417.2 25.7 228.1 362.0 24 | 1 1 22 7 22 14 0 1 -7.0 33.161 -112.837 1587.4 807.0 317.2 298.3 0.0 255.1 27.6 380.3 104.6 25 | 1 1 22 7 22 13 0 1 -8.0 33.164 -112.953 1503.7 819.0 317.2 299.6 0.0 156.2 27.1 332.7 20.4 26 | 1 1 22 7 22 12 0 1 -9.0 33.166 -113.048 1368.2 840.4 317.4 302.0 0.0 142.5 22.7 222.8 0.0 27 | 1 1 22 7 22 11 0 1 -10.0 33.149 -113.140 1183.9 858.5 317.4 303.9 0.0 110.5 21.5 236.5 0.0 28 | 1 1 22 7 22 10 0 1 -11.0 33.124 -113.227 1126.5 861.3 317.1 303.8 0.0 93.9 24.0 256.7 0.0 29 | 1 1 22 7 22 9 0 1 -12.0 33.105 -113.330 1163.7 862.0 316.7 303.5 0.0 80.4 26.6 211.2 0.0 30 | 1 1 22 7 22 8 0 1 -13.0 33.113 -113.436 1201.5 857.0 316.8 303.1 0.0 155.4 25.9 229.9 0.0 31 | 1 1 22 7 22 7 0 1 -14.0 33.137 -113.526 1223.7 851.2 316.7 302.5 0.0 143.2 28.3 268.5 0.0 32 | 1 1 22 7 22 6 0 1 -15.0 33.184 -113.585 1042.1 862.6 317.1 303.9 0.0 171.7 24.2 329.0 0.0 33 | 1 1 22 7 22 5 0 1 -16.0 33.261 -113.711 955.8 860.2 317.2 303.8 0.0 334.6 25.8 455.0 0.0 34 | 1 1 22 7 22 4 0 1 -17.0 33.370 -113.857 980.9 833.0 318.1 301.9 0.0 576.0 22.2 703.2 0.0 35 | 1 1 22 7 22 3 0 1 -18.0 33.487 -114.028 1171.0 828.6 316.8 300.2 0.0 1209.2 31.9 563.9 0.0 36 | 1 1 22 7 22 2 0 1 -19.0 33.491 -114.220 1130.4 852.8 316.0 301.9 0.0 1680.5 35.0 355.2 83.7 37 | 1 1 22 7 22 1 0 1 -20.0 33.438 -114.392 986.4 864.4 315.8 302.9 0.0 1684.0 34.6 368.3 284.2 38 | 1 1 22 7 22 0 0 1 -21.0 33.362 -114.536 1062.9 866.3 315.5 302.8 0.0 1722.1 33.1 280.9 497.2 39 | 1 1 22 7 21 23 0 1 -22.0 33.291 -114.670 1227.1 871.4 315.3 303.1 0.0 2004.0 31.8 69.4 678.0 40 | 1 1 22 7 21 22 0 1 -23.0 33.247 -114.793 1268.9 858.0 314.9 301.4 0.0 1799.3 33.3 173.8 836.2 41 | 1 1 22 7 21 21 0 1 -24.0 33.226 -114.898 1417.1 834.7 314.6 298.8 0.0 1559.4 36.9 269.3 946.6 -------------------------------------------------------------------------------- /examples/knote_ae_2015/README.txt: -------------------------------------------------------------------------------- 1 | Typical Summer Day 2 | ------------------ 3 | 4 | This example application is based on the Knote et al. (2015) simulation of a typical summer day with CB05. The example here uses the "Summer" PBL and temperature profile. At present, the example uses the a relatively "clean" initial environment (NOx(t=0) = 1ppb). 5 | 6 | Where possible, the inputs are as described in the publication, but has been adapted for pykpp. The model uses initial conditions, temperature, PBL and deposition rates as described in the publications. The emissions were produced from the NEI using a similar process as described in the paper, but may be somewhat different. 7 | 8 | Figures showing the inputs and outputs are distributed with the example and will be remade if you run the example (python summerclean.py). physical.pdf shows the input diurnal profiles of temperature, pressure, NOx emissions, and photolysis. chemical_ozone.pdf shows the output diurnal profiles of ozone, hydroxyl radical and hydroperoxy radical concentrations. chemical_nox.pdf shows the output diurnal profiles of NO, NO2 and the sum. 9 | 10 | [1] Knote, C., Tuccella, P., Curci, G., Emmons, L., Orlando, J. J., Madronich, S., Baró, R., Jiménez-Guerrero, P., Luecken, D., Hogrefe, C., Forkel, R., Werhahn, J., Hirtl, M., Pérez, J. L., San José, R., Giordano, L., Brunner, D., Yahya, K., Zhang, Y., Influence of the choice of gas-phase mechanism on predictions of key gaseous pollutants during the AQMEII phase-2 intercomparison, Atmospheric Environment, Volume 115, August 2015, Pages 553-568, ISSN 1352-2310, http://dx.doi.org/10.1016/j.atmosenv.2014.11.066. 11 | -------------------------------------------------------------------------------- /examples/knote_ae_2015/chemical_nox.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/barronh/pykpp/7afea7d55a37089cb6ae94b47cd8c3f2e377ccff/examples/knote_ae_2015/chemical_nox.pdf -------------------------------------------------------------------------------- /examples/knote_ae_2015/chemical_ozone.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/barronh/pykpp/7afea7d55a37089cb6ae94b47cd8c3f2e377ccff/examples/knote_ae_2015/chemical_ozone.pdf -------------------------------------------------------------------------------- /examples/knote_ae_2015/mean_emis.csv: -------------------------------------------------------------------------------- 1 | time,emis_ALD2,emis_ALDX,emis_CH4,emis_CL2,emis_CO,emis_ETH,emis_ETHA,emis_ETOH,emis_FORM,emis_HONO,emis_IOLE,emis_ISOP,emis_MEOH,emis_NH3,emis_NO,emis_NO2,emis_OLE,emis_PAR,emis_SESQ,emis_SO2,emis_SULF,emis_TERP,emis_TOL,emis_XYL 2 | 0,0.009782475,0.003378729,0.020594137,0.000272824,0.37623799,0.023582038,0.007752421,0.023938367,0.019756878,0.000450755,0.021942778,1.45E-05,0.002004891,0.041319199,0.068131328,0.00608949,0.025636181,0.24303871,0.003029566,0.001919644,1.89E-05,0.045247756,0.00192445,0.001446171 3 | 3600,0.009269881,0.0032036,0.019553497,0.000272834,0.32818484,0.022291822,0.007261382,0.022668259,0.018739702,0.000411843,0.020815786,1.39E-05,0.001900662,0.037867632,0.063159242,0.005604534,0.02427806,0.22741607,0.002748217,0.001779459,1.74E-05,0.043026295,0.00172492,0.001283684 4 | 7200,0.008881351,0.003073562,0.018970681,0.000272834,0.30460587,0.02133316,0.00693836,0.021744553,0.017963199,0.00039342,0.019950897,1.35E-05,0.00185463,0.035278447,0.060696919,0.005375989,0.023270898,0.21735588,0.002538632,0.001714331,1.68E-05,0.041290648,0.001632145,0.001203938 5 | 10800,0.008591107,0.002977591,0.018979864,0.000272834,0.30461517,0.020663343,0.006783442,0.021096248,0.017369084,0.000394752,0.019283054,1.34E-05,0.001868521,0.034206063,0.060659263,0.0053617,0.022538668,0.21222222,0.002376886,0.001724916,1.68E-05,0.039900161,0.001632853,0.001209816 6 | 14400,0.00840356,0.002918046,0.019908005,0.000272834,0.34498599,0.02033582,0.006851513,0.02078783,0.016961612,0.000430036,0.018799039,1.35E-05,0.001985938,0.035258923,0.064797856,0.005742832,0.022105472,0.21442311,0.00225107,0.001861314,1.82E-05,0.03879549,0.001797843,0.001358705 7 | 18000,0.008343155,0.002901453,0.022410128,0.000272834,0.46381518,0.020544937,0.007251957,0.020866441,0.016797915,0.0005429,0.018501209,1.45E-05,0.002223165,0.039177805,0.078018345,0.007044623,0.022069845,0.2274044,0.002151683,0.002175239,2.11E-05,0.037914127,0.002234988,0.001740913 8 | 21600,0.008527562,0.002950619,0.02944573,0.000272834,0.7587198,0.022062209,0.00824565,0.021550326,0.017064305,0.00078599,0.018472647,1.71E-05,0.002596334,0.046137121,0.10642745,0.009708844,0.02294993,0.26199538,0.002071935,0.002704222,2.57E-05,0.037205171,0.003360614,0.002751546 9 | 25200,0.008872467,0.00305861,0.037166651,0.000272834,1.0607874,0.023958923,0.009545066,0.02283122,0.017616058,0.001037785,0.01880401,0.021962324,0.009088424,0.057192154,0.13623437,0.012554428,0.024532588,0.30827147,0.002053483,0.003383002,3.20E-05,0.037129797,0.004724654,0.003915238 10 | 28800,0.009908378,0.003417295,0.042040549,0.000272835,1.2450563,0.026664114,0.011078685,0.025921132,0.019626651,0.001144652,0.020871295,0.13182302,0.039924491,0.080533311,0.15080106,0.013865525,0.028943172,0.3704859,0.002435438,0.004075115,3.91E-05,0.040815506,0.006003666,0.004858927 11 | 32400,0.01153492,0.004000422,0.043332323,0.000272915,1.2782941,0.029842407,0.012566859,0.030337812,0.023141641,0.001148439,0.024265334,0.31593689,0.090160221,0.11650111,0.15452959,0.014207507,0.035369094,0.4373154,0.003246322,0.004801556,4.54E-05,0.047556099,0.006973036,0.005446536 12 | 36000,0.01335725,0.004615865,0.046615507,0.000272915,1.3895245,0.034096107,0.01419939,0.035597891,0.026702911,0.001178404,0.028349241,0.5450322,0.15056099,0.15976873,0.16054235,0.014713961,0.043290563,0.51586396,0.004384552,0.005242257,5.00E-05,0.055760641,0.008269147,0.006318918 13 | 39600,0.015615944,0.00535727,0.050479781,0.000272915,1.5749049,0.039174389,0.015844386,0.041545704,0.031381834,0.001265538,0.032959241,0.77642488,0.21019702,0.21179843,0.17306401,0.015715556,0.051798798,0.60138506,0.00588856,0.005777623,5.34E-05,0.065139778,0.00978181,0.007329615 14 | 43200,0.017697172,0.006018857,0.051736854,0.000272915,1.6621436,0.043585144,0.017154993,0.047652166,0.035413872,0.00131411,0.037562151,0.95384985,0.25580093,0.26889476,0.17984706,0.016356928,0.059111703,0.67998534,0.007650892,0.006030382,5.56E-05,0.07465861,0.011191724,0.008119715 15 | 46800,0.01968235,0.00665568,0.05211981,0.000272915,1.7419443,0.04731591,0.018123912,0.053350482,0.039634973,0.001333135,0.041526932,1.0717148,0.28622419,0.31614092,0.18322529,0.016765121,0.064769298,0.75297475,0.009312714,0.0064124,5.68E-05,0.082655624,0.01275739,0.008930598 16 | 50400,0.021065587,0.007121058,0.052576717,0.000272915,1.8206626,0.050213508,0.018856104,0.056329347,0.042651605,0.001374647,0.04438417,1.1613028,0.30939859,0.3437407,0.18842466,0.017402757,0.069039784,0.78472793,0.010671123,0.006663485,5.75E-05,0.088669002,0.012952305,0.009060179 17 | 54000,0.021391066,0.00728568,0.054924425,0.000272915,1.9346312,0.052497655,0.019451642,0.053521834,0.043252796,0.00147321,0.045736935,1.1959649,0.31912696,0.34420964,0.1985981,0.018415576,0.071747303,0.7255944,0.011596781,0.006418471,5.71E-05,0.092522345,0.009716567,0.007607257 18 | 57600,0.021330031,0.007263036,0.054834191,0.000272914,1.947726,0.052940838,0.019397901,0.052573193,0.042878173,0.001493039,0.046090234,1.1816198,0.3166762,0.31628537,0.19963324,0.018646769,0.072141118,0.70173365,0.01188756,0.006059296,5.52E-05,0.093533635,0.008596766,0.007045309 19 | 61200,0.020899456,0.007104703,0.053011887,0.000272834,1.8719484,0.051753178,0.01872712,0.051361471,0.042049959,0.001410339,0.045173012,1.0626224,0.28715369,0.26640081,0.18946755,0.01763518,0.069356225,0.67550474,0.01150501,0.005726701,5.17E-05,0.091609389,0.008222965,0.006712356 20 | 64800,0.019732006,0.00672382,0.048284043,0.000272834,1.6153185,0.048476547,0.017490914,0.04858743,0.039728101,0.001217071,0.042773042,0.80277997,0.22070459,0.20017156,0.16687171,0.015318842,0.0626477,0.61982036,0.010462536,0.005340824,4.89E-05,0.086720094,0.007385984,0.005977158 21 | 68400,0.017811073,0.006097009,0.042218629,0.000272834,1.2911766,0.043538246,0.016031291,0.044305194,0.035693359,0.001027421,0.039002884,0.46274507,0.13057083,0.13035157,0.14410914,0.013128786,0.053043053,0.54306191,0.00885566,0.004865478,4.71E-05,0.078990936,0.006198367,0.004971926 22 | 72000,0.015458625,0.005311952,0.036849238,0.000272834,1.0341462,0.037657332,0.014120041,0.038332816,0.031069109,0.000873546,0.033873722,0.153542,0.045728941,0.087083057,0.12478221,0.011244194,0.04231663,0.4527176,0.006771201,0.004296729,4.21E-05,0.068258449,0.005004237,0.004010708 23 | 75600,0.015623343,0.005367485,0.036857709,0.000272834,1.0381364,0.038028665,0.014204639,0.038712759,0.031397924,0.000874477,0.034257643,0.14647564,0.043634467,0.088372529,0.12421087,0.011257282,0.04254448,0.45512038,0.006919931,0.004297151,4.21E-05,0.068990365,0.005007808,0.004012344 24 | 79200,0.013115701,0.004512786,0.030426241,0.000272834,0.76678634,0.032042973,0.011785425,0.032678299,0.026192056,0.000727186,0.029170364,0.001867817,0.003497483,0.068014733,0.10419447,0.009458482,0.034530554,0.36657169,0.005108554,0.003308851,3.38E-05,0.059019871,0.003752127,0.002968316 25 | 82800,0.011551979,0.003981011,0.026003392,0.000272834,0.59681475,0.028166441,0.009876447,0.02851272,0.023196843,0.000613494,0.025751919,1.70E-05,0.002535017,0.055128727,0.088980615,0.008020524,0.030399693,0.30710772,0.004059906,0.00262117,2.63E-05,0.052552596,0.002899305,0.002262177 26 | 86400,0.010504967,0.003624736,0.022805884,0.000272824,0.46728343,0.025486125,0.008573514,0.025795296,0.02116897,0.000518541,0.023491064,1.55E-05,0.002193674,0.046334345,0.076687194,0.006885039,0.02759476,0.26867488,0.003431679,0.002171598,2.15E-05,0.048235204,0.002343145,0.001789401 27 | -------------------------------------------------------------------------------- /examples/knote_ae_2015/physical.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/barronh/pykpp/7afea7d55a37089cb6ae94b47cd8c3f2e377ccff/examples/knote_ae_2015/physical.pdf -------------------------------------------------------------------------------- /examples/knote_ae_2015/plot_knoteae2015.py: -------------------------------------------------------------------------------- 1 | """ 2 | pykpp Tutorial 3 | ============== 4 | 5 | goals: Predict ozone at a monitor using HYSPLIT and pykpp 6 | authors: Barron Henderson, Robert Nedbor-Gross, Qian Shu 7 | 8 | # Overview 9 | 10 | 1. Create an idealized environment (Knote et al., 2015) 11 | 2. Create a simple trajectory model 12 | - Chemistry - CB05 13 | - Emissions from NEI 14 | - Deposition from Knote et al., 2015 15 | - Dilution following Knote et al., 2015 16 | 17 | Assumes pykpp is installed 18 | 19 | python -m pip install http://github.com/barronh/pykpp/archive/main.zip 20 | 21 | Knote et al. 2015 22 | ----------------- 23 | 24 | This example application is based on the Knote et al. (2015) simulation of a typical summer day with CB05. The example here uses the "Summer" PBL and temperature profile. At present, the example uses the a relatively "clean" initial environment (NOx(t=0) = 1ppb). 25 | 26 | Trajectory model 27 | ---------------- 28 | 29 | We'll use a KPP language to implement the model. Each piece will be saved as a string and then combined to produce an active model. 30 | 31 | - prepare an air pracel and model environment, 32 | - select gas-phase chemical reactions, 33 | - add emissions as chemical reactions, 34 | - add deposition as chemical reactions 35 | 36 | """ 37 | 38 | import time 39 | import numpy as np 40 | from pykpp.mech import Mech 41 | from io import StringIO 42 | import pandas as pd 43 | import matplotlib.pyplot as plt 44 | from pykpp.updaters import func_updater 45 | 46 | 47 | infile = StringIO(""" 48 | #INLINE PY_INIT 49 | TEMP = 298.15 50 | P = 99600. 51 | t = TSTART = 0 * 3600. 52 | TEND = TSTART + 3600. * 24 53 | DT = 60. 54 | MONITOR_DT = 3600 55 | StartDate = 'datetime(2010, 7, 14)' 56 | Latitude_Degrees = 40. 57 | Longitude_Degrees = 0.00E+00 58 | KDIL = 0. 59 | PBLH_OLD = 250. 60 | vd_CO = 0.03 61 | vd_SO2 = 1.8 62 | vd_NO = 0.016 63 | vd_NO2 = 0.1 64 | vd_NO3 = 0.1 65 | vd_N2O5 = 4 66 | vd_HONO = 2.2 67 | vd_HNO3 = 4 68 | vd_HNO4 = 4 69 | vd_O3 = 0.4 70 | vd_H2O2 = 0.5 71 | vd_CH3OOH = 0.1 72 | molps_to_molpcm2ps = Avogadro / 1200000**2 73 | 74 | add_world_updater(func_updater(Update_M, incr=360., verbose=False)) 75 | add_world_updater(func_updater(Update_THETA, incr=360., verbose=False)) 76 | add_world_updater(interpolated_from_csv( 77 | 'summerenv.tsv', 'time', incr = 360., delimiter = '\\t', verbose=False 78 | )) 79 | add_world_updater(interpolated_from_csv( 80 | 'mean_emis.csv', 'time', incr=360, verbose=False 81 | )) 82 | add_world_updater(func_updater( 83 | Monitor, incr=7200., allowforce=False, verbose=False 84 | )) 85 | #ENDINLINE 86 | 87 | #INITVALUES 88 | CFACTOR = P * Avogadro / R / TEMP * centi **3 * nano {ppb-to-molecules/cm3} 89 | ALL_SPEC=1e-32*CFACTOR; 90 | TOTALNOx=1. 91 | M=1e9 92 | O2=.21*M 93 | N2=.79*M 94 | H2O=0.01*M 95 | CH4=1750 96 | CO=100 97 | O3=30. 98 | NO = 0.1 * TOTALNOx 99 | NO2 = 0.9 * TOTALNOx 100 | SO2 = 1 101 | N2O = 320 102 | {B = 210.; what to do about B?} 103 | 104 | #MONITOR t/3600.; THETA; PBLH; TEMP; O3; 105 | #LOOKAT THETA; t/3600; PBLH; TEMP; O1D; OH; HO2; O3; NO; NO2; eNOx; TUV_J(6, THETA); emis_NO; emis_NO * molps_to_molpcm2ps / PBLH / 100 / CFACTOR * 3600; 106 | 107 | #include cb05cl.eqn 108 | EMISSION = ALD2 : emis_ALD2 * molps_to_molpcm2ps / PBLH / 100 ; 109 | EMISSION = ALDX : emis_ALDX * molps_to_molpcm2ps / PBLH / 100 ; 110 | EMISSION = CH4 : emis_CH4 * molps_to_molpcm2ps / PBLH / 100 ; 111 | EMISSION = CL2 : emis_CL2 * molps_to_molpcm2ps / PBLH / 100 ; 112 | EMISSION = CO : emis_CO * molps_to_molpcm2ps / PBLH / 100 ; 113 | EMISSION = ETH : emis_ETH * molps_to_molpcm2ps / PBLH / 100 ; 114 | EMISSION = ETHA : emis_ETHA * molps_to_molpcm2ps / PBLH / 100 ; 115 | EMISSION = ETOH : emis_ETOH * molps_to_molpcm2ps / PBLH / 100 ; 116 | EMISSION = FORM : emis_FORM * molps_to_molpcm2ps / PBLH / 100 ; 117 | EMISSION = HONO : emis_HONO * molps_to_molpcm2ps / PBLH / 100 ; 118 | EMISSION = IOLE : emis_IOLE * molps_to_molpcm2ps / PBLH / 100 ; 119 | EMISSION = ISOP : emis_ISOP * molps_to_molpcm2ps / PBLH / 100 ; 120 | EMISSION = MEOH : emis_MEOH * molps_to_molpcm2ps / PBLH / 100 ; 121 | EMISSION = NH3 : emis_NH3 * molps_to_molpcm2ps / PBLH / 100 ; 122 | EMISSION = NO + eNOx : emis_NO * molps_to_molpcm2ps / PBLH / 100 ; 123 | EMISSION = NO2 + eNOx : emis_NO2 * molps_to_molpcm2ps / PBLH / 100 ; 124 | EMISSION = OLE : emis_OLE * molps_to_molpcm2ps / PBLH / 100 ; 125 | EMISSION = PAR : emis_PAR * molps_to_molpcm2ps / PBLH / 100 ; 126 | EMISSION = SESQ : emis_SESQ * molps_to_molpcm2ps / PBLH / 100 ; 127 | EMISSION = SO2 : emis_SO2 * molps_to_molpcm2ps / PBLH / 100 ; 128 | EMISSION = SULF : emis_SULF * molps_to_molpcm2ps / PBLH / 100 ; 129 | EMISSION = TERP : emis_TERP * molps_to_molpcm2ps / PBLH / 100 ; 130 | EMISSION = TOL : emis_TOL * molps_to_molpcm2ps / PBLH / 100 ; 131 | EMISSION = XYL : emis_XYL * molps_to_molpcm2ps / PBLH / 100 ; 132 | CO = DUMMY : vd_CO / PBLH / 100 ; 133 | SO2 = DUMMY : vd_SO2 / PBLH / 100 ; 134 | NO = DUMMY : vd_NO / PBLH / 100 ; 135 | NO2 = DUMMY : vd_NO2 / PBLH / 100 ; 136 | NO3 = DUMMY : vd_NO3 / PBLH / 100 ; 137 | N2O5 = DUMMY : vd_N2O5 / PBLH / 100 ; 138 | HONO = DUMMY : vd_HONO / PBLH / 100 ; 139 | HNO3 = DUMMY : vd_HNO3 / PBLH / 100 ; 140 | PNA = DUMMY : vd_HNO4 / PBLH / 100 ; 141 | O3 = DUMMY : vd_O3 / PBLH / 100 ; 142 | H2O2 = DUMMY : vd_H2O2 / PBLH / 100 ; 143 | MEPX = DUMMY : vd_CH3OOH / PBLH / 100 ; 144 | 145 | """) 146 | 147 | keywords = [ 148 | 'DUMMY', 'EMISSION', 'BNZHRXN', 'BNZNRXN', 'ISOPRXN', 'SESQRXN', 'SULRXN', 149 | 'TOLHRXN', 'TOLNRXN', 'TRPRXN', 'XYLHRXN', 'XYLNRXN' 150 | ] 151 | 152 | mech = Mech( 153 | infile, mechname='summercb05', incr=360, add_default_funcs=False, 154 | keywords=keywords 155 | ) 156 | 157 | # Create time start/stop matrix 158 | nhour = 24. 159 | start_end_ts = np.linspace( 160 | 0, nhour, int(nhour) * 12 + 1 161 | ).repeat(2, 0)[1:-1].reshape(-1, 2) * 3600 162 | 163 | # Capture initial values as "background" 164 | # concentrations 165 | mech.world['ybkg'] = mech.get_y(mech.parsed_world) 166 | 167 | # Create specific tolerance values for radical species 168 | # great care should be taken with O1D. 169 | # note that CMAQ EBI solver has no absolute tolerance and 170 | # uses 1 rtol for radicals and rxn counters. 171 | # 172 | atol = np.array([ 173 | 10 if (spc in ('O', 'O1D', 'HCO3', 'NTR') or spc[-3:] == 'RXN') else 1e-3 174 | for spc in mech.allspcs 175 | ]) 176 | rtol = np.array([ 177 | 0.1 if (spc in ('O', 'O1D', 'HCO3', 'NTR') or spc[-3:] == 'RXN') else 1e-5 178 | for spc in mech.allspcs 179 | ]) 180 | 181 | # Start timing the process 182 | runstart = time.time() 183 | 184 | # Update world for all interpolated values 185 | mech.world['t'] = start_end_ts.min() 186 | mech.update_y_from_world() 187 | mech.Update_World(forceupdate=True) 188 | 189 | # %% 190 | # Define a function to dilute with PBL 191 | # ------------------------------------ 192 | # - In a trajectory model, we need a PBL dilution 193 | # - It should only dilute when PBL goes up 194 | # - When PBL collapses, it should not concentrate 195 | # - This will be called by the mechanism in between integrations 196 | 197 | def UpdatePBL(mech, world): 198 | """ 199 | Defining updater for PBL rise 200 | """ 201 | if 'y' not in world: 202 | return 203 | PBLH_OLD = world['PBLH_OLD'] 204 | ybkg = world['ybkg'] 205 | y = world['y'] 206 | PBLH = mech.world['PBLH'] 207 | if PBLH != PBLH_OLD: 208 | KDIL = np.minimum(1, PBLH_OLD / PBLH) 209 | mech.world['KDIL'] = KDIL 210 | world['PBLH_OLD'] = PBLH 211 | ydil = (KDIL * y) + (1 - KDIL) * ybkg 212 | nonzero = ybkg != 2.4195878568940516e-22 213 | y[nonzero] = ydil[nonzero] 214 | else: 215 | mech.world['KDIL'] = 1 216 | mech.update_world_from_y(y) 217 | 218 | # Add an updater that depends on the y vector 219 | mech.add_world_updater(func_updater(UpdatePBL, incr=360., verbose=False)) 220 | mech.world['PBLH_OLD'] = mech.world['PBLH'] 221 | 222 | # %% 223 | # Run Model 224 | # --------- 225 | # 226 | 227 | # Archive initial values 228 | mech.archive() 229 | 230 | # For each start/stop combination, update the world and integrate 231 | for t0, t1 in start_end_ts: 232 | # set current time to t0 233 | t = t0 234 | # Get y vector from world state 235 | y = mech.update_y_from_world() 236 | # integrate from t to t1 with initial state of y 237 | # ts, Y = mech.integrate( 238 | # t, t1, y0=y, solver='odeint', mxords=3, mxordn=3, atol=atol, 239 | # rtol=rtol, mxstep = 1000, hmax = 300, verbose=False 240 | # ) 241 | ts, Y = mech.integrate( 242 | t, t1, y0=y, solver='lsoda', atol=atol, rtol=rtol, nsteps=1000, 243 | max_step=300, max_order_ns=3, max_order_s=3, verbose=False 244 | ) 245 | # Update world to match new time 246 | mech.world['y'] = Y[-1] 247 | mech.update_world_from_y() 248 | mech.Update_World(forceupdate=True) 249 | # Update y vector for mixing of PBL 250 | # Set new time to last time of integration 251 | t = ts[-1] 252 | # Update world from y vector 253 | mech.update_world_from_y() 254 | mech.archive() 255 | 256 | 257 | # Optionally save to disk 258 | # mech.output() 259 | 260 | runend = time.time() 261 | print((runend - runstart), 'seconds') 262 | 263 | # %% 264 | # Make Plots 265 | # ---------- 266 | 267 | data = mech.get_output() 268 | data['h_LST'] = data['t'] / 3600 269 | data['NOx'] = data['NO'] + data['NO2'] 270 | data['jNO2*100'] = data['TUV_J(6,THETA)'] * 100 271 | data['PBLH [km]'] = data['PBLH'] / 1e3 272 | data['TEMP/25 [C]'] = (data['TEMP'] - 273.15) / 25. 273 | data['OH/10 [ppqv]'] = data['OH'] * 1e5 274 | data['HO2 [pptv]'] = data['HO2'] * 1e3 275 | 276 | fig, ax = plt.subplots() 277 | data.set_index('h_LST')[['emis_NO', 'jNO2*100', 'PBLH [km]', 'TEMP/25 [C]']].plot(ax=ax) 278 | fig.savefig('knote_physical.png') 279 | 280 | fig, ax = plt.subplots() 281 | data.set_index('h_LST')[['NOx', 'NO', 'NO2']].plot(ax=ax) 282 | ax.set(xlabel='hour [LST]', ylabel='ppb', ylim=(.1, 5), yscale='log') 283 | fig.savefig('chemical_nox.png') 284 | 285 | fig, ax = plt.subplots() 286 | data.set_index('h_LST')[['O3', 'OH/10 [ppqv]', 'HO2 [pptv]']].plot(ax=ax) 287 | ax.set(xlabel='hour (LST)') 288 | fig.savefig('chemical_ozone.png') 289 | -------------------------------------------------------------------------------- /examples/knote_ae_2015/summerenv.tsv: -------------------------------------------------------------------------------- 1 | time PBLH TEMP 2 | 0.00 250. 288.15 3 | 3600 250. 288.15 4 | 7200 250. 288.15 5 | 10800 250. 288.15 6 | 14400 250. 288.15 7 | 18000 300. 288.16 8 | 21600 550. 289 9 | 25200 850. 292.75 10 | 28800 1120 295.93 11 | 32400 1300 298.52 12 | 36000 1400 300.55 13 | 39600 1475 302 14 | 43200 1500 302.86 15 | 46800 1500 303.15 16 | 50400 1500 302.86 17 | 54000 1500 302 18 | 57600 1500 300.55 19 | 61200 1500 298.53 20 | 64800 1500 295.93 21 | 68400 1500 292.75 22 | 72000 1500 289 23 | 75600 250. 288.15 24 | 79200 250. 288.15 25 | 82800 250. 288.15 26 | 86400 250. 288.15 -------------------------------------------------------------------------------- /examples/plot_acp2006.py: -------------------------------------------------------------------------------- 1 | """ 2 | Simple Chemistry Example 3 | ======================== 4 | 5 | This test is a simple example from Seinfeld and Pandis ACP Second edition 6 | on pg 240.[1]. 7 | 8 | we extract a basic chemical mechanism with 1 primary organic oxidation 9 | (R1). Under high NOx conditions, the primary organic oxidation produces 10 | 2 peroxy radical NO oxidations (R2,R3) to produce more radicals (i.e., 11 | HO2 or OH). The produced NO2 can photolyze (R4) to reproduce NO and an 12 | odd oxygen (O3P) that is assumed to instantaneously produce ozone. Under 13 | high-NOx conditions, the ozone can be lost by oxidizing NO (R5). Finally, 14 | radicals can be removed lost by producing nitric acid (R6) or peroxides 15 | (R7,R8). Lastly, we add an artificial source of HOx (here defined as HO2 16 | + OH) (R9). 17 | 18 | [Seinfeld and Pandis Pg 240](https://books.google.com/books?id=YH2K9eWsZOcC&lpg=PT220&vq=RH%20PHOx&pg=PT220#v=onepage&f=false) 19 | """ 20 | 21 | # %% 22 | # Imports 23 | # ------- 24 | # 25 | 26 | import io 27 | import matplotlib.pyplot as plt 28 | from pykpp.mech import Mech 29 | import numpy as np 30 | 31 | # %% 32 | # Define Model 33 | # ------------ 34 | # A model is made up of: 35 | # 36 | # - equations to be solved 37 | # - inline code to be run before equations are solved (PY_INIT), 38 | # - options for saving data, and 39 | # - state initialization (INITVALUES). 40 | # 41 | 42 | mechstr = """ 43 | #EQUATIONS 44 | {R1} RH + OH = RO2 + H2O : 26.3e-12; 45 | {R2} RO2 + NO = NO2 + RCHO + HO2 : 7.7e-12; 46 | {R3} HO2 + NO = NO2 + OH : 8.1e-12; 47 | {R4} NO2 = NO + O3 : jno2 ; 48 | {R5} O3 + NO = NO2 + O2 : 1.9e-14 ; 49 | {R6} OH + NO2 = HNO3 : kohno2 ; 50 | {R7} HO2 + HO2 = H2O2 + O2 : 2.9e-12 ; 51 | {R8} RO2 + HO2 = ROOH + O2 : 5.2e-12 ; 52 | {R9} EMISSION = OH : PHOx; 53 | 54 | #INLINE PY_INIT 55 | t=TSTART=6*3600 56 | TEND=10*3600+TSTART 57 | P = 99600. 58 | TEMP = 298.15 59 | DT = 60. 60 | MONITOR_DT = 3600. 61 | StartDate = 'datetime(2010, 7, 14)' 62 | Latitude_Degrees = 40. 63 | Longitude_Degrees = 0.00E+00 64 | 65 | jno2 = .015; 66 | kohno2 = 1.1e-11; 67 | #ENDINLINE 68 | 69 | #MONITOR O3; RH; NO; NO2; OH; HO2; 70 | #INTEGRATOR odeint; 71 | 72 | #INITVALUES 73 | CFACTOR = P * Avogadro / R / TEMP * centi **3 * nano {ppb-to-molecules/cm3} 74 | ALL_SPEC=1e-32*CFACTOR; 75 | M=1e9 76 | TOTALNOx=10. 77 | RH = 200. 78 | O2=.21*M 79 | N2=.79*M 80 | H2O=0.01*M 81 | O3=30. 82 | NO = TOTALNOx * 2 / 3 83 | NO2 = TOTALNOx * 1 / 3 84 | PHOx = .1e-3 * CFACTOR 85 | {B = 210.; what to do about B?} 86 | """ 87 | 88 | # %% 89 | # Run the model and visualize 90 | # --------------------------- 91 | # - load the model in a mechanism, 92 | # - run it using the odeint solver, 93 | # - get the output as a pandas dataframe, 94 | # - add some derived variables for plotting, and 95 | # - make one plot for NOx species, and one for ozone, OH, and HO2 96 | 97 | mech = Mech(io.StringIO(mechstr), incr=3600, verbose=0) 98 | runtime = mech.run(verbose=1, debug=False, solver='odeint') 99 | df = mech.get_output() 100 | 101 | df['h_LST'] = df.eval('t / 3600') 102 | df['NOx'] = df.eval('NO + NO2') 103 | df['OH/10 ppqv'] = df.eval('OH * 1e5') 104 | df['HO2 pptv'] = df.eval('HO2 * 1e3') 105 | 106 | fig, ax = plt.subplots() 107 | df.set_index('h_LST')[['NO', 'NO2', 'NOx']].plot(ax=ax) 108 | ax.set(yscale='log') 109 | fig.savefig('simple_nox.png') 110 | 111 | fig, ax = plt.subplots() 112 | df.set_index('h_LST')[['O3', 'OH/10 ppqv', 'HO2 pptv']].plot(ax=ax) 113 | fig.savefig('simple_ox.png') 114 | 115 | # %% 116 | # Create Ozone Isopleths like Figure 6.12 117 | # --------------------------------------- 118 | # - reload the model in a mechanism and disable monitoring, 119 | # - define the NOx and VOC increments to run to find ozone points, 120 | # - run each combniation of NOx and VOC it using the vode solver, 121 | # - save ozone as a 2d array, 122 | # - make a plot of the contours of ozone from all NOx and VOC combos. 123 | 124 | mech = Mech(io.StringIO(mechstr), monitor_incr=None) 125 | noxppbs = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30 , 40, 50] 126 | vocppbs = [50, 60, 70, 80, 90, 100, 200, 300, 400, 500] 127 | 128 | o3vals = np.zeros((len(noxppbs), len(vocppbs)), dtype = 'f') 129 | 130 | for ni, n in enumerate(noxppbs): 131 | print(f'NOx [ppb]: {n}', end=': VOC [ppb]:') 132 | for vi, v in enumerate(vocppbs): 133 | print(v, end='.', flush=True) 134 | # reset world to ensure no carryover from previous simulation 135 | mech.resetworld() 136 | CFACTOR = mech.world['CFACTOR'] 137 | # override the default initialization of NO, NO2 and RH (VOC) 138 | mech.world['NO'] = n * CFACTOR * 2 / 3 139 | mech.world['NO2'] = n * CFACTOR * 1 / 3 140 | mech.world['RH'] = v * CFACTOR 141 | # Run for this combination of NOx and VOC and store result 142 | mech.run(solver = 'vode') 143 | o3vals[ni, vi] = eval('O3 / CFACTOR', None, mech.world) 144 | print() 145 | 146 | fig, ax = plt.subplots() 147 | levels = np.linspace(30, 360, 12) 148 | cs = ax.contourf(vocppbs, noxppbs, o3vals, levels=levels) 149 | fig.colorbar(cs, label='ozone ppb') 150 | ax.set_yscale('log', base=10, subs=[2,3,5]) 151 | ax.set_xscale('log', base=10, subs=[2,3,5]) 152 | ax.set(title='Ozone Isoplets (PHOx=0.1 ppt/s)', xlabel='RH [ppb]', ylabel='NOx [ppb]') 153 | fig.savefig('ekma.png') 154 | 155 | -------------------------------------------------------------------------------- /examples/plot_henderson2011.py: -------------------------------------------------------------------------------- 1 | """ 2 | Upper Tropospheric Simulation 3 | ============================= 4 | 5 | goals: Predict ozone in the Upper Troposphere 6 | author: Barron Henderson 7 | last updated: 2025-05-02 8 | 9 | Henderson et al. (2011) used an ensemble of simulation to create a syntheic 10 | upper troposphere. Each simulation was initialized by observed composition from 11 | a set of measurements identified as "fresh convection." The simulation the aged 12 | for 10 days. The ensemble was then sampled to mimic the effect of convection. 13 | 14 | This examples shows how to use a single simulation based on the median of 15 | observations from Table 2. 16 | 17 | Henderson et al. (2011) doi:10.5194/acp-11-275-2011 18 | """ 19 | 20 | import time 21 | import numpy as np 22 | from pykpp.mech import Mech 23 | from io import StringIO 24 | import pandas as pd 25 | import matplotlib.pyplot as plt 26 | from pykpp.updaters import func_updater 27 | 28 | mdnobs_ppb = pd.read_csv(StringIO("""spc,bkg,init 29 | HO,0.5396e-3,0.6101e-3 30 | HO2,13.16e-3,11.24e-3 31 | O3,77.76,70.61 32 | NO2,95.52e-3,153.6e-3 33 | NO,203.3e-3,411.8e-3 34 | HNO3,280.1e-3,125.9e-3 35 | HO2NO2,82.e-3,67.8e-3 36 | H2O2,234.2e-3,195.9e-3 37 | CO,98.36,108.0 38 | CH4,1.789e3,1.784e3 39 | C2H6,790.e-3,800e-3 40 | C3H8,146e-3,153.5e-3 41 | C2H4,1.5e-3,1.5e-3 42 | RNO3,8.63e-3,8.63e-3 43 | CH2O,174.5e-3,437e-3 44 | CH3CHO,83.8e-3,117.5e-3 45 | CH3COCH3,1475e-3,1375e-3 46 | CH3COC2H5,71.25e-3,95.e-3 47 | CH3CONO2,374.9e-3,370.6e-3 48 | CH3COOOH,172.8e-3,226.1e-3 49 | """)) 50 | 51 | mdnobs_ppb = mdnobs_ppb.set_index('spc').rename(index={ 52 | 'HO': 'OH', 'HO2NO2': 'HNO4', 'C2H6': 'ETHA', 'C3H8': 'PRPA', 53 | 'C2H4': 'ETH', 'RNO3': 'NTR1', 'CH2O': 'FORM', 'CH3CHO': 'ALD2', 54 | 'CH3COCH3': 'ACET', 'CH3COC2H5': 'KET', 'CH3CONO2': 'PAN', 55 | 'CH3COOOH': 'PAA' 56 | }).groupby('spc').sum() 57 | print(list(mdnobs_ppb.index)) 58 | inittxt = '\n'.join([f'{k} = {v}' for k, v in mdnobs_ppb.init.items()]) 59 | 60 | infile = StringIO(""" 61 | #INLINE PY_INIT 62 | TEMP = 233.7 { median upper trop temp } 63 | P = 30060. { median upper trop pressure } 64 | t = TSTART = 12 * 3600. { noon start} 65 | TEND = TSTART + 3600. * 24 * 10 { run for 10 days } 66 | DT = 60. { solve at 60s } 67 | MONITOR_DT = 3600 { save at 1h } 68 | StartDate = 'datetime(2010, 7, 14)' 69 | Latitude_Degrees = 40. { latitude is nominally mid-latitudes } 70 | Longitude_Degrees = 0.00E+00 { longitude is nominal so LST = UTC } 71 | 72 | add_world_updater(func_updater(Update_M, incr=360., verbose=False)) 73 | add_world_updater(func_updater(Update_THETA, incr=360., verbose=False)) 74 | add_world_updater(func_updater( 75 | Monitor, incr=7200., allowforce=False, verbose=False 76 | )) 77 | #ENDINLINE 78 | 79 | #INITVALUES 80 | CFACTOR = P * Avogadro / R / TEMP * centi**3 * nano {ppb-to-molecules/cm3} 81 | ALL_SPEC=1e-32*CFACTOR; 82 | TOTALNOx=1. 83 | M=1e9 84 | O2=.21*M 85 | N2=.79*M 86 | H2O=0.01*M 87 | 88 | """ + inittxt + """ 89 | 90 | #MONITOR t//3600; THETA; TEMP; O3; 91 | #LOOKAT ALL; 92 | 93 | #include cb6r5m_ae7_aq.eqn 94 | 95 | """) 96 | 97 | keywords = [ 98 | 'DUMMY', 'EMISSION', 'BNZHRXN', 'BNZNRXN', 'ISOPRXN', 'SESQRXN', 'SULRXN', 99 | 'TOLHRXN', 'TOLNRXN', 'TRPRXN', 'XYLHRXN', 'XYLNRXN' 100 | ] 101 | 102 | mech = Mech( 103 | infile, mechname='test', incr=360, add_default_funcs=False, 104 | keywords=keywords 105 | ) 106 | 107 | unused_obs = [spc for spc in list(mdnobs_ppb.index) if spc not in mech.allspcs] 108 | if len(unused_obs) > 0: 109 | print('**WARN:: unused obs') 110 | print('**', unused_obs) 111 | print('**WARN:: unused obs') 112 | 113 | # Create time start/stop matrix 114 | nhour = 24. * 10 115 | start_end_ts = np.linspace( 116 | 0, nhour, int(nhour) * 12 + 1 117 | ).repeat(2, 0)[1:-1].reshape(-1, 2) * 3600 + 12 * 3600 118 | 119 | 120 | # Create specific tolerance values for radical species 121 | # great care should be taken with O1D. 122 | # note that CMAQ EBI solver has no absolute tolerance and 123 | # uses 1 rtol for radicals and rxn counters. 124 | # 125 | fastspc = ['O', 'O1D', 'HCO3', 'NTR1'] 126 | fastspc += [s for s in mech.allspcs if s.endswith('RXN')] 127 | atol = np.array([10 if spc in fastspc else 1e-3 for spc in mech.allspcs]) 128 | rtol = np.array([0.1 if spc in fastspc else 1e-5 for spc in mech.allspcs]) 129 | 130 | # Start timing the process 131 | runstart = time.time() 132 | 133 | # Update world for all interpolated values 134 | mech.world['t'] = start_end_ts.min() 135 | mech.update_y_from_world() 136 | mech.Update_World(forceupdate=True) 137 | 138 | # %% 139 | # Run Model 140 | # --------- 141 | # 142 | 143 | # Archive initial values 144 | mech.archive() 145 | 146 | # For each start/stop combination, update the world and integrate 147 | for t0, t1 in start_end_ts: 148 | # set current time to t0 149 | t = t0 150 | # Get y vector from world state 151 | y = mech.update_y_from_world() 152 | # integrate from t to t1 with initial state of y 153 | ts, Y = mech.integrate( 154 | t, t1, y0=y, solver='odeint', mxords=3, mxordn=3, atol=atol, 155 | rtol=rtol, mxstep = 1000, hmax = 300, verbose=False 156 | ) 157 | # ts, Y = mech.integrate( 158 | # t, t1, y0=y, solver='lsoda', atol=atol, rtol=rtol, nsteps=1000, 159 | # max_step=300, max_order_ns=3, max_order_s=3, verbose=False 160 | # ) 161 | mixdt, = np.diff(ts) 162 | # 5% per day; converted to % per second and % per dt 163 | mech.world['y'] = Y[-1] 164 | if False: 165 | mixf = .05 / 24 / 3600 * mixdt 166 | Ybkg = np.copy(Y[-1]) 167 | for s, b in mdnobs_ppb.bkg.items(): 168 | if s in mech.allspcs: 169 | # convert ppb to #/cm3 170 | Ybkg[mech.allspcs.index(s)] = b * mech.world['CFACTOR'] 171 | # Update world to match new time 172 | mixed = Y[-1] * (1 - mixf) + mixf * Ybkg 173 | mech.world['y'][:] = mixed 174 | 175 | mech.update_world_from_y() 176 | mech.Update_World(forceupdate=True) 177 | # Update y vector for mixing of PBL 178 | # Set new time to last time of integration 179 | t = ts[-1] 180 | # Update world from y vector 181 | mech.update_world_from_y() 182 | mech.archive() 183 | 184 | 185 | # Optionally save to disk 186 | # mech.output() 187 | 188 | runend = time.time() 189 | print((runend - runstart), 'seconds') 190 | 191 | # %% 192 | # Make Plots 193 | # ---------- 194 | 195 | data = mech.get_output() 196 | data['h_LST'] = data['t'] / 3600 197 | noxspcs = ['NO', 'NO2', 'N2O5', 'HONO', 'NO3'] 198 | data['NOx'] = sum([data[ns] for ns in noxspcs if ns in data.columns]) 199 | data['TEMP/25 [C]'] = (data['TEMP'] - 273.15) / 25. 200 | data['OH/10 [ppqv]'] = data['OH'] * 1e5 201 | data['HO2 [pptv]'] = data['HO2'] * 1e3 202 | 203 | fig, ax = plt.subplots() 204 | data.set_index('h_LST')[['NOx'] + noxspcs].plot(ax=ax) 205 | ax.set(xlabel='hour [LST]', ylabel='ppb', ylim=(.1, 5), yscale='log') 206 | fig.savefig('henderson_nox.png') 207 | 208 | fig, ax = plt.subplots() 209 | data.set_index('h_LST')[['O3', 'OH/10 [ppqv]', 'HO2 [pptv]']].plot(ax=ax) 210 | ax.set(xlabel='hour (LST)') 211 | fig.savefig('henderson_ox.png') 212 | -------------------------------------------------------------------------------- /examples/plot_zombies.py: -------------------------------------------------------------------------------- 1 | """ 2 | Zombie Apocalypse 3 | ================= 4 | 5 | author: Barron H. Henderson 6 | date: 2020-02-11 7 | Inspired by numerous mathematics examples using Zombies (e.g., [1,2,3]), this 8 | is a test of the pykpp system to solve for a zombie outbreak. 9 | 10 | The basic model is that zombies (Z) both kill and infect humans (H), and 11 | humans kill zombies. I have also added a natural death and birth rate for 12 | humans, though it makes little difference on the time scales here. I have 13 | made additional assumptions as follows: 14 | 15 | 1. uniform distribution of humans and zombies, 16 | 2. interactions are proportional to density, 17 | 3. like infectous diseases, the zombie infection rates increase in winter 18 | 4. human militarization against zombies increases with the total zombies. 19 | 20 | To run this exmaple, you must have installed pykpp 21 | 22 | python -m pip install https://github.com/barronh/pykpp/archive/main.zip 23 | 24 | 25 | [1] https://people.maths.ox.ac.uk/maini/PKM%20publications/384.pdf 26 | 27 | [2] https://www.ncbi.nlm.nih.gov/pmc/articles/PMC4798798/ 28 | 29 | [3] https://www.amazon.com/dp/0691173206/ref=cm_sw_em_r_mt_dp_688KKQ5HMHJW9G6Z6654 30 | """ 31 | 32 | import io 33 | import pykpp 34 | import pandas as pd 35 | import numpy as np 36 | import matplotlib.pyplot as plt 37 | 38 | 39 | # %% 40 | # Define Reactions 41 | # ----------------- 42 | # 43 | # * R1 : Zombies infect humans with a seasonal variation 44 | # * k_inf is set later as a constant 45 | # * A multiplier ranging from 0.25 in the summer to 1.75 in the winter 46 | # * R2 : zombies kill humans using a constant coefficient 47 | # * R3 : humans kill zombies using a coefficient made of two parts: 48 | # * k_hkillz : humans naturally defend themselves killing zombies 49 | # * Z * AREA : the military or police gets involved proportional to the total 50 | # zombie population. 51 | # * Right now, human militarization increase with zombie population, but it 52 | # decreases immediately in response. This is unrealistic. It would be good 53 | # to add a "memory" so that the zombies are removed more effectively. 54 | # * R4, R5 : humans naturally procreate and die 55 | # 56 | 57 | mechanism = """ 58 | #EQUATIONS 59 | {R1} Zombie + Human = 2 Zombie + Infected : k_inf * (np.cos(np.radians(t / 365 * 360)) * 0.5 + 1); {infections increase in winter} 60 | {R2} Zombie + Human = Zombie + KilledHuman : k_zkillh; 61 | {R3} Zombie + Human = Human + KilledZombie : k_hkillz * (Zombie * AREA); {humans militarize against zombies proportional to total zombie population} 62 | {R4} Human = 2 Human : k_hbirth; 63 | {R5} Human = DeadHuman : k_hdeath; 64 | 65 | """ 66 | 67 | # %% 68 | # Initialize the model 69 | # ----------------- 70 | # 71 | # * time (t) is initialized at the start 72 | # * the end is set to 10 years hences 73 | # * Latitude/Longitude/Temperature/M are unused, but added to satisfy 74 | # exectations of pykpp 75 | # * AREA is set to USA area in km2 76 | # * constant rate coefficients are set 77 | # * monitoring the Human and Zombie population 78 | # * using the scipy.ode integrator 79 | # * Finally setting the initial values 80 | # * CFACTOR is used to convert populations to population/km2 81 | # * Humans (H) is set to an approximate USA population 82 | # * Zombies (Z) is set to a very small initial population 83 | # 84 | 85 | 86 | setup = """ 87 | #INLINE PY_INIT 88 | t=TSTART=0 # days 89 | TEND=365.25*10+TSTART 90 | TEMP = 288.; # unused 91 | M = 0.; # unused 92 | DT = 3/24. 93 | StartDate = 'datetime(2010, 7, 14)' 94 | Latitude_Degrees = 40. # Unused 95 | Longitude_Degrees = 0.00E+00 # Unused 96 | AREA = 9.834e6 {USA area in km2} 97 | k_inf = 0.5/365 # 0.5 km2/person/year 98 | k_zkillh = 0.2/365 # 0.2 km2/person/year 99 | k_hkillz = 0.1/365 # 0.1 km2/person/year 100 | k_hbirth = 0.01/365 # 1% per year 101 | k_hdeath = 0.01/365 # 1% per year 102 | #ENDINLINE 103 | """ 104 | config = """ 105 | #MONITOR Human; Zombie; 106 | #INTEGRATOR odeint; 107 | """ 108 | init = """ 109 | #INITVALUES 110 | CFACTOR = 1./AREA {people-to-people/km2 based on USA area} 111 | ALL_SPEC=1e-32*CFACTOR; 112 | Human = 300e6 {approximate US population} 113 | Zombie = 10 114 | """ 115 | mechdef = setup + '\n' + config + '\n' + init + '\n' + mechanism 116 | 117 | 118 | # %% 119 | # Load the model 120 | # -------------- 121 | # 122 | # * Using IO to create an in-memory file 123 | # * Creating a mechanism: 124 | # * called 'zombies', so the output will be 'zombies.pykpp.tsv' 125 | # * solving at a 1 day increment 126 | # * monitoring as it is solved per year 127 | 128 | mech = pykpp.mech.Mech( 129 | io.StringIO(mechdef), mechname='zombies', incr=1, monitor_incr=365, 130 | verbose=0 131 | ) 132 | 133 | 134 | # %% 135 | # Reset and Run 136 | # ------------- 137 | # 138 | # * Run the model and print the total time to solve 139 | 140 | runtime = mech.run(verbose=1, debug=False, solver='odeint'); # vode or lsoda 141 | print('Runtime (s)', runtime) 142 | 143 | # %% 144 | # Plot the Zombie Statistics 145 | # -------------------------- 146 | # 147 | # * Using pandas to read the file 148 | # * Using matplotlib for control over plotting. 149 | # * note that the seasonality of infection rate causes the wobble. 150 | 151 | def zombieplot(tsvpath, timelabel='Years'): 152 | data = pd.read_csv(tsvpath, delimiter='\t', index_col='t') 153 | if timelabel.lower() == 'years': 154 | tfac = 1 / 365.25 155 | elif timelabel.lower() == 'days': 156 | tfac = 1 157 | else: 158 | raise KeyError(f'timelabel must be Years or Days; got {timelabel}') 159 | 160 | t = data.index.values * tfac 161 | kH = data.Human / 1e6 162 | Z = data.Zombie 163 | dZ = data.KilledZombie 164 | kI = data.Infected 165 | gskw = dict(wspace=0.2, left=.2) 166 | fig, axx = plt.subplots(1, 3, figsize=(14, 4), gridspec_kw=gskw) 167 | plt.setp(axx, xlabel=timelabel) 168 | plt.rc('lines', marker='o', linestyle='none', markersize=1) 169 | axx[0].plot(t, kH, label='Human Population (1e6)') 170 | mfmt = plt.matplotlib.ticker.FuncFormatter('{0:.5f}'.format) 171 | axx[0].yaxis.set_major_formatter(mfmt) 172 | axx[1].plot(t, Z, label='Zombies Population') 173 | axx[2].plot(t[1:], np.diff(dZ), label='Zombies Killed Rate (per day)') 174 | axx[2].plot(t[1:], np.diff(kI), label='Infection Rate (per day)') 175 | for ai, ax in enumerate(axx): 176 | ax.legend() 177 | axx[1].set_ylim(0, None); 178 | axx[2].set_ylim(0, None); 179 | return fig 180 | 181 | fig = zombieplot('zombies.pykpp.tsv') 182 | fig.savefig('zombies.pykpp.png') 183 | 184 | # %% 185 | # Re run with adjustments parameters 186 | # ---------------------------------- 187 | # 188 | 189 | 190 | mech = pykpp.mech.Mech( 191 | io.StringIO(mechdef), mechname='zombies2', incr=1, monitor_incr=365, 192 | verbose=0 193 | ) 194 | # Increase initial Zombies by 2 and the infection rate by 20 195 | mech.world['Zombie'] *= 2 196 | mech.world['k_inf'] *= 20 197 | runtime = mech.run(verbose=1, debug=False, solver='odeint'); # vode or lsoda 198 | 199 | fig = zombieplot('zombies2.pykpp.tsv'); 200 | fig.savefig('zombies2.pykpp.png') 201 | 202 | 203 | # %% 204 | # Equations of the End: Teaching Mathematical Modeling Using the Zombie Apocalypse 205 | # -------------------------------------------------------------------------------- 206 | # 207 | # Lofgren et al. 2016[1] published a teaching example and an on-line 208 | # playground[2] with solutions. This offers a really cool opportunity 209 | # to check our results. 210 | # 211 | # We get the exact same results -- hooray! Try changing the parameters. For 212 | # example, adjust the parameters to represent COVID. Consider adding effects 213 | # of temperature and humidity in transmission. 214 | # 215 | # [1] https://www.ncbi.nlm.nih.gov/pmc/articles/PMC4798798/#s1-jmbe-17-134 216 | # 217 | # [2] http://cartwrig.ht/apps/whitezed/ 218 | 219 | 220 | import pykpp 221 | import pandas as pd 222 | zeddef = """ 223 | #EQUATIONS 224 | {R1} Zombie + Susceptible = Zombie + Infected : k_inf; 225 | {R2} Zombie + Susceptible = Zombie + KilledHuman : k_zkillh; 226 | {R3} Zombie + Susceptible = Susceptible + KilledZombie : k_hkillz; 227 | {R5} Zombie = DecayedZombie : k_zdeath; 228 | {R6} Susceptible = Vaccinated : k_vaccination; 229 | {R7} Infected = Zombie : k_incubation; 230 | 231 | #INLINE PY_INIT 232 | t=TSTART=0 # days 233 | TEND=TSTART + 60 234 | TEMP = 288.; # unused, but represents average surface temperature 235 | P = 101325.; # unused, but represents average surface pressure 236 | M = P / 8.314 / TEMP * 6.022e23; # unused 237 | 238 | DT = 1/24. 239 | 240 | StartDate = 'datetime(2010, 7, 14)' 241 | Latitude_Degrees = 40. # Unused 242 | Longitude_Degrees = 0.00E+00 # Unused 243 | 244 | k_inf = 1. {infection rate} 245 | k_zkillh = 0.0 {zombie kill human rate} 246 | k_hkillz = 0.0 {human kill zombie rate} 247 | k_zdeath = 0.0 {zombie decay} 248 | k_vaccination = 0.0 {rate of susceptible vaccination} 249 | incubation_time_days = 1e-3 {must be greater than zero} 250 | k_incubation = 1 / (incubation_time_days) { tau = 1 / k; so tau = 1 seconds} 251 | #ENDINLINE 252 | 253 | #MONITOR Susceptible; Vaccinated; Zombie; 254 | #INTEGRATOR odeint; 255 | 256 | #INITVALUES 257 | CFACTOR = 1./10001 { denominator should match sum of people = Susceptible + Vaccinated + Zombie} 258 | ALL_SPEC=1e-32*CFACTOR; 259 | Susceptible = 10000 260 | Vaccinated = 0 261 | Zombie = 1 262 | """ 263 | mech = pykpp.mech.Mech( 264 | io.StringIO(zeddef), mechname='zed', incr=1/24, monitor_incr=5, verbose=0 265 | ) 266 | runtime = mech.run(verbose=1, debug=False, solver='odeint'); # vode or lsoda 267 | 268 | data = pd.read_csv('zed.pykpp.tsv', delimiter='\t', index_col='t') 269 | ax = data.drop(['TEMP', 'P', 'CFACTOR'], axis=1).plot() 270 | ax.figure.savefig('zed.pykpp.png') 271 | -------------------------------------------------------------------------------- /pykpp/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = '1.0.0' 2 | __all__ = ['mech', 'parse', 'plot', 'stdfuncs', 'main', 'funcs'] 3 | import warnings 4 | import sys 5 | from . import mech 6 | from . import parse 7 | from . import plot 8 | from . import funcs 9 | from . import stdfuncs 10 | from . import main 11 | 12 | warn = warnings.warn 13 | 14 | 15 | def clean_showwarning( 16 | message, category, filename, lineno, file=None, line=None 17 | ): 18 | print( 19 | '**PYKPP:%s:%s:%s:\n %s' 20 | % ((filename), lineno, category.__name__, message), file=sys.stderr 21 | ) 22 | return 23 | 24 | 25 | warnings.showwarning = clean_showwarning 26 | -------------------------------------------------------------------------------- /pykpp/__main__.py: -------------------------------------------------------------------------------- 1 | from .main import main 2 | 3 | if __name__ == '__main__': 4 | main(globals=globals()) 5 | -------------------------------------------------------------------------------- /pykpp/funcs/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = [ 2 | 'am3', 'geoschem', 'geoschemkpp', 'kpp', 'camx', 'cmaq', 'mozart4', 'racm', 3 | 'chimere' 4 | ] 5 | from . import am3 6 | from . import geoschem 7 | from . import geoschemkpp 8 | from . import kpp 9 | from . import cmaq 10 | from . import camx 11 | from . import mozart4 12 | from . import racm 13 | from . import chimere 14 | -------------------------------------------------------------------------------- /pykpp/funcs/am3.py: -------------------------------------------------------------------------------- 1 | __all__ = [ 2 | 'MCM_KBPAN', 'MCM_KFPAN', 'AM3_STD', 'AM3_TROE', 'AM3_USR1', 'AM3_USR2', 3 | 'AM3_USR3', 'AM3_USR4', 'AM3_USR5', 'AM3_USR6', 'AM3_USR7', 'AM3_USR8', 4 | 'AM3_USR8a', 'AM3_USR9', 'AM3_USR10', 'AM3_USR11', 'AM3_USR12', 5 | 'AM3_USR13', 'AM3_USR14', 'AM3_USR15', 'AM3_USR16', 'AM3_USR17', 6 | 'AM3_USR18', 'AM3_USR19', 'AM3_USR21', 'AM3_USR22', 'AM3_USR24', 7 | 'AM3_USR25', 'AM3_USR51', 'AM3_USR52', 'AM3_USR53', 'AM3_USR54', 8 | 'AM3_USR57', 'AM3_USR58', 'AM3_USR59', 'AM3_USR60', 'AM3_USR61' 9 | ] 10 | from numpy import log10, exp 11 | 12 | O2 = N2 = M = TEMP = INVTEMP = 0 13 | H2O = T300I = 0 14 | 15 | 16 | def update_func_world(mech, world): 17 | """ 18 | Function to update globals for user defined functions 19 | """ 20 | global O2, N2, M, TEMP, INVTEMP 21 | globals().update(world) 22 | globals()['INVTEMP'] = 1 / world['TEMP'] 23 | globals()['T300I'] = 300. / world['TEMP'] 24 | 25 | 26 | def MCM_KBPAN(): 27 | KD0 = 1.10e-05 * M * exp(-10100 * INVTEMP) 28 | KDI = 1.90e17 * exp(-14100 * INVTEMP) 29 | KRD = KD0 / KDI 30 | FCD = 0.30 31 | NCD = 0.75-1.27*(log10(FCD)) 32 | FD = 10**(log10(FCD)/(1+(log10(KRD)/NCD)**2)) 33 | KBPAN = (KD0*KDI)*FD/(KD0+KDI) 34 | return KBPAN 35 | 36 | 37 | def MCM_KFPAN(): 38 | KC0 = 3.28e-28*M*(TEMP/300)**-6.87 39 | KCI = 1.125e-11*(TEMP/300)**-1.105 40 | KRC = KC0/KCI 41 | FCC = 0.30 42 | NC = 0.75-1.27*(log10(FCC)) 43 | FC = 10**(log10(FCC)/(1+(log10(KRC)/NC)**2)) 44 | KFPAN = (KC0*KCI)*FC/(KC0+KCI) 45 | return KFPAN 46 | 47 | 48 | def AM3_STD(A0, B0, C0): 49 | return A0 * exp(B0*INVTEMP) 50 | 51 | 52 | def AM3_TROE(A0, B0, A1, B1, factor): 53 | ko = A0 * (T300I)**B0 54 | kinf = A1 * (T300I)**B1 55 | xpo = ko * M / kinf 56 | AM3_TROE = ko * M / (1.0 + xpo) 57 | xpo = log10(xpo) 58 | xpo = 1.0 / (1.0 + xpo*xpo) 59 | return AM3_TROE * factor**xpo 60 | 61 | 62 | def AM3_USR1(): 63 | return 6.e-34 * (T300I)**2.4 64 | 65 | 66 | def AM3_USR2(): 67 | return AM3_TROE(2.0e-30, 4.4, 1.4e-12, .7, .6) 68 | 69 | 70 | def AM3_USR3(): 71 | return AM3_USR2() * 3.704e26 * exp(-11000.0*INVTEMP) 72 | 73 | 74 | def AM3_USR4(): 75 | return AM3_TROE(1.8e-30, 3.0, 2.8e-11, 0.0, .6) 76 | 77 | 78 | def AM3_USR5(): 79 | TINV = 1*INVTEMP 80 | ko = M * 6.5e-34 * exp(1335.*TINV) 81 | ko = ko / (1. + ko/(2.7e-17*exp(2199.*TINV))) 82 | return ko + 2.4e-14*exp(460.*TINV) 83 | 84 | 85 | def AM3_USR6(): 86 | return AM3_TROE(2.0e-31, 3.4, 2.9e-12, 1.1, .6) 87 | 88 | 89 | def AM3_USR7(): 90 | return AM3_USR6() * exp(-10900.*INVTEMP) / 2.1e-27 91 | 92 | 93 | def AM3_USR8(): 94 | return AM3_TROE(5.9e-33, 1.4, 1.1e-12, -1.3, .6) 95 | 96 | 97 | def AM3_USR8a(): 98 | return AM3_TROE(1.5e-13, -0.6, 2.1e9, -6.1, .6) / M 99 | 100 | 101 | def AM3_USR9(): 102 | tinv = 1.0*INVTEMP 103 | ko = 2.3e-13 * exp(600.0*tinv) 104 | kinf = 1.7e-33 * M * exp(1000.0*tinv) 105 | fc = 1.0 + 1.4e-21 * H2O * exp(2200.0*tinv) 106 | 107 | return (ko + kinf) * fc 108 | 109 | 110 | def AM3_USR10(): 111 | return AM3_TROE(8.e-27, 3.5, 3.e-11, 0, .5) 112 | 113 | 114 | def AM3_USR11(): 115 | return AM3_TROE(9.7e-29, 5.6, 9.3e-12, 1.5, .6) 116 | 117 | 118 | def AM3_USR12(): 119 | return AM3_USR11() * 1.111e28 * exp(-14000.0 * INVTEMP) 120 | 121 | 122 | def AM3_USR13(): 123 | return AM3_TROE(1.e-28, 4.5, 7.5e-12, 0.85, .6) 124 | 125 | 126 | def AM3_USR14(): 127 | return 9.3e-12 * T300I / M 128 | 129 | 130 | def AM3_USR15(): 131 | return AM3_USR14() * 1.111e28 * exp(-14000.0 * INVTEMP) 132 | 133 | 134 | def AM3_USR16(): 135 | return 0 136 | 137 | 138 | def AM3_USR17(): 139 | return 0 140 | 141 | 142 | def AM3_USR18(): 143 | return 0 144 | 145 | 146 | def AM3_USR19(): 147 | return 0 148 | 149 | 150 | def AM3_USR21(): 151 | return TEMP**2 * 7.69e-17 * exp(253.0*INVTEMP) 152 | 153 | 154 | def AM3_USR22(): 155 | return 3.82e-11 * exp(-2000.0*INVTEMP) + 1.33e-13 156 | 157 | 158 | def AM3_USR24(): 159 | ko = 1.0 + 5.5e-31 * exp(7460.0*INVTEMP) * M * 0.21 160 | return 1.7e-42 * exp(7810.0*INVTEMP) * M * 0.21 / ko 161 | 162 | 163 | def AM3_USR25(): 164 | return 0.0 165 | 166 | 167 | def AM3_USR51(): 168 | return AM3_TROE(9.0e-28, 8.9, 7.7e-12, .2, .6) 169 | 170 | 171 | def AM3_USR52(): 172 | return AM3_USR51()*1.111e28 * exp(-14000.0 * INVTEMP) 173 | 174 | 175 | def AM3_USR53(): 176 | return AM3_TROE(9.0e-28, 8.9, 7.7e-12, .2, .6) 177 | 178 | 179 | def AM3_USR54(): 180 | return AM3_USR53()*1.111e28 * exp(-14000.0 * INVTEMP) 181 | 182 | 183 | def AM3_USR57(): 184 | FRAC = 1-11.0729*exp(-(1./73.)*TEMP) 185 | if (FRAC < 0): 186 | FRAC = 0.01 187 | return AM3_STD(8.00e-12, 0.0, 0.0)*FRAC 188 | 189 | 190 | def AM3_USR58(): 191 | FRAC = 1-11.0729*exp(-(1./73.)*TEMP) 192 | if (FRAC < 0): 193 | FRAC = 0.01 194 | return AM3_STD(8.00e-12, 0.0, 0.0)*(1.0-FRAC) 195 | 196 | 197 | def AM3_USR59(): 198 | K1 = AM3_STD(1.40e-12, -1860.0, 0.0) 199 | return K1 * (O2 + 3.5e18) / (2.0 * O2 + 3.5e18) 200 | 201 | 202 | def AM3_USR60(): 203 | FRAC = 1-23.7*exp(-(1./60)*TEMP) 204 | if (FRAC < 0): 205 | FRAC = 0.01 206 | return AM3_STD(2.15e-12, 305, 0.0)*FRAC 207 | 208 | 209 | def AM3_USR61(): 210 | FRAC = 1-23.7*exp(-(1./60)*TEMP) 211 | if (FRAC < 0): 212 | FRAC = 0.01 213 | return AM3_STD(2.15e-12, 305, 0.0)*(1.0-FRAC) 214 | -------------------------------------------------------------------------------- /pykpp/funcs/camx.py: -------------------------------------------------------------------------------- 1 | __all__ = ['CAMX_4', 'CAMX_6'] 2 | 3 | from numpy import exp, log 4 | 5 | TEMP = M = 0 6 | 7 | 8 | def update_func_world(mech, world): 9 | """ 10 | Function to update globals for user defined functions 11 | """ 12 | global M, TEMP 13 | globals().update(world) 14 | 15 | 16 | def CAMX_4(A0, Ea0, B0, Tr0, A1, Ea1, B1, Tr1, F, n): 17 | k0 = A0 * (TEMP/Tr0)**(B0) * exp(-Ea0/TEMP) 18 | kinf = A1 * (TEMP/Tr1)**(B1) * exp(-Ea1/TEMP) 19 | k0M = k0*M 20 | G = (1+(log(k0M/kinf)/n)**2)**(-1) 21 | k = (k0M / (1 + k0M/kinf)) * F**(G) 22 | return k 23 | 24 | 25 | def CAMX_6(A0, Ea0, B0, Tr0, A2, Ea2, B2, Tr2, A3, Ea3, B3, Tr3): 26 | k0 = A0 * (TEMP/Tr0)**(B0) * exp(-Ea0/TEMP) 27 | k2 = A2 * (TEMP/Tr2)**(B2) * exp(-Ea2/TEMP) 28 | k3 = A3 * (TEMP/Tr3)**(B3) * exp(-Ea3/TEMP) 29 | k = k0 + k3*M/(1+k3*M/k2) 30 | return k 31 | -------------------------------------------------------------------------------- /pykpp/funcs/chimere.py: -------------------------------------------------------------------------------- 1 | __all__ = ['CHIMERE_MTROE', 'CHIMERE_TROE', 'CHIMERE_JO3', 'CHIMERE_SPECIAL_1', 2 | 'CHIMERE_SPECIAL_2', 'CHIMERE_SPECIAL_3', 'CHIMERE_SPECIAL_4'] 3 | 4 | from numpy import exp, log10 5 | H2O = O2 = N2 = M = TEMP = 0 6 | INVTEMP = T300I = 0 7 | 8 | 9 | def update_func_world(mech, world): 10 | """ 11 | Function to update globals for user defined functions 12 | """ 13 | global H2O, O2, N2, M, TEMP, INVTEMP, T300I 14 | globals().update(world) 15 | 16 | 17 | def CHIMERE_TROE(A0, B0, C0, A1, B1, C1, N): 18 | """ 19 | Mapping: 20 | A0 = tabrate(1,nr) 21 | B0 = tabrate(2,nr) 22 | C0 = tabrate(3,nr) 23 | A1 = tabrate(4,nr) 24 | B1 = tabrate(5,nr) 25 | C1 = tabrate(6,nr) 26 | N = tabrate(7,nr) 27 | 28 | M = ai; M = third body concentration (molecules/cm3) and must be 29 | defined in the stdfuncs namespace 30 | 31 | TEMP = te = bulk air temperature 32 | 33 | 1. = dun 34 | Original Code: 35 | c1 = tabrate(1,nr)*exp(-tabrate(2,nr)/te) & 36 | *(300d0/te)**tabrate(3,nr) 37 | c2 = tabrate(4,nr)*exp(-tabrate(5,nr)/te) & 38 | *(300d0/te)**tabrate(6,nr) 39 | c3 = ai*c1 40 | c4 = c3/c2 41 | ex = dun/(dun + log10(c4)**2) 42 | rate(nr,izo,ime,ivert) = c1*tabrate(7,nr)**ex/(dun + c4) 43 | """ 44 | 45 | c1 = A0*exp(-B0/TEMP)*(300./TEMP)**C0 46 | c2 = A1*exp(-B1/TEMP)*(300./TEMP)**C1 47 | c3 = M*c1 48 | c4 = c3/c2 49 | ex = 1./(1. + log10(c4)**2) 50 | out = c1*N**ex/(1. + c4) 51 | return out 52 | 53 | 54 | def CHIMERE_MTROE(A0, B0, C0, A1, B1, C1, N): 55 | """ 56 | Mapping: 57 | A0 = tabrate(1,nr) 58 | B0 = tabrate(2,nr) 59 | C0 = tabrate(3,nr) 60 | A1 = tabrate(4,nr) 61 | B1 = tabrate(5,nr) 62 | C1 = tabrate(6,nr) 63 | N = tabrate(7,nr) 64 | M = ai 65 | TEMP = te 66 | 1. = dun 67 | Original Code: 68 | c1 = tabrate(1,nr)*exp(-tabrate(2,nr)/te) & 69 | *(300d0/te)**tabrate(3,nr) 70 | c2 = tabrate(4,nr)*exp(-tabrate(5,nr)/te) & 71 | *(300d0/te)**tabrate(6,nr) 72 | c3 = ai*c1 73 | c4 = c3/c2 74 | ex = dun/(dun + ((log10(c4) - 0.12d0)/1.2d0)**2) 75 | rate(nr,izo,ime,ivert) = c1*tabrate(7,nr)**ex/(dun + c4) 76 | """ 77 | c1 = A0*exp(-B0/TEMP)*(300./TEMP)**C0 78 | c2 = A1*exp(-B1/TEMP)*(300./TEMP)**C1 79 | c3 = M*c1 80 | c4 = c3/c2 81 | ex = 1./(1. + ((log10(c4) - 0.12)/1.2)**2) 82 | out = c1*N**ex/(1. + c4) 83 | return out 84 | 85 | 86 | def CHIMERE_JO3(rate): 87 | ai = M 88 | te = TEMP 89 | # Chimere does not use specific humidity, so this 90 | # is commented out in favor of H2O concentration 91 | # the units of sphu in the rates.F90 are #/cm**-3 92 | # 93 | # Ma = (O2 * 32. + N2 * 28.) / Avogadro 94 | # Mh2o = H2O * 18. / Avogadro 95 | # hu = Mh2o / Ma 96 | # factor = hu/(hu + ai*(0.02909*exp(70./te) + 0.06545*exp(110./te))) 97 | factor = H2O/(H2O + ai*(0.02909*exp(70./te) + 0.06545*exp(110./te))) 98 | return rate*factor 99 | 100 | 101 | def CHIMERE_SPECIAL_1(A1, C1, A2, C2): 102 | """ 103 | f1 = A1*exp(-C1/TEMP) 104 | f2 = A2*exp(-C2/TEMP) 105 | rate = f1 * f2/(1. + f2) 106 | """ 107 | f1 = A1*exp(-C1/TEMP) 108 | f2 = A2*exp(-C2/TEMP) 109 | rate = f1 * f2/(1. + f2) 110 | return rate 111 | 112 | 113 | def CHIMERE_SPECIAL_2(A1, C1, A2, C2): 114 | """ 115 | f1 = A1*exp(-C1/TEMP) 116 | f2 = A2*exp(-C2/TEMP) 117 | rate = f1/(1. + f2) 118 | """ 119 | f1 = A1*exp(-C1/TEMP) 120 | f2 = A2*exp(-C2/TEMP) 121 | rate = f1/(1. + f2) 122 | return rate 123 | 124 | 125 | def CHIMERE_SPECIAL_3(A1, C1, A2, C2, A3, C3, A4, C4): 126 | """ 127 | f1 = A1*exp(-C1/TEMP) 128 | f2 = A2*exp(-C2/TEMP) 129 | f3 = A3*exp(-C3/TEMP) 130 | f4 = A4*exp(-C4/TEMP) 131 | rate = 2.*(f1 * f2 * f3 * f4/((1.+f3)*(1.+f4)))**(0.5) 132 | """ 133 | f1 = A1*exp(-C1/TEMP) 134 | f2 = A2*exp(-C2/TEMP) 135 | f3 = A3*exp(-C3/TEMP) 136 | f4 = A4*exp(-C4/TEMP) 137 | rate = 2.*(f1 * f2 * f3 * f4/((1.+f3)*(1.+f4)))**(0.5) 138 | return rate 139 | 140 | 141 | def CHIMERE_SPECIAL_4(A1, C1, A2, C2, A3, C3, A4, C4): 142 | """ 143 | f1 = A1*exp(-C1/TEMP) 144 | f2 = A2*exp(-C2/TEMP) 145 | f3 = A3*exp(-C3/TEMP) 146 | f4 = A4*exp(-C4/TEMP) 147 | f3 = f3 / (1. + f3) 148 | f4 = f4 / (1. + f4) 149 | rate = 2.0*(f1*f2)**(0.5)*(1.-(f3*f4)**(0.5))*(1.-f4)/(2.-f3-f4) 150 | """ 151 | f1 = A1*exp(-C1/TEMP) 152 | f2 = A2*exp(-C2/TEMP) 153 | f3 = A3*exp(-C3/TEMP) 154 | f4 = A4*exp(-C4/TEMP) 155 | f3 = f3 / (1. + f3) 156 | f4 = f4 / (1. + f4) 157 | rate = 2.0*(f1*f2)**(0.5)*(1.-(f3*f4)**(0.5))*(1.-f4)/(2.-f3-f4) 158 | return rate 159 | -------------------------------------------------------------------------------- /pykpp/funcs/cmaq.py: -------------------------------------------------------------------------------- 1 | __all__ = ['CMAQ_1to4', 'CMAQ_5', 'CMAQ_6', 'CMAQ_7', 2 | 'CMAQ_8', 'CMAQ_9', 'CMAQ_10', 'CMAQ_10D', 'OH_CO'] 3 | from numpy import exp, log10 4 | 5 | # Globals must be udpated by update_func_world for this 6 | # to work 7 | P = M = TEMP = 0 8 | 9 | 10 | def update_func_world(mech, world): 11 | """ 12 | Function to update globals for user defined functions 13 | """ 14 | globals().update(world) 15 | 16 | 17 | def CMAQ_1to4(A0, B0, C0): 18 | """ 19 | CMAQ reaction rates form 1-4 have the form K = A * (T/300.0)**B * EXP(-C/T) 20 | """ 21 | # REAL A0, B0, C0 22 | 23 | return (A0 * (TEMP/300.0)**B0 * exp(-C0/TEMP)) 24 | 25 | 26 | def CMAQ_5(A0, B0, C0, Kf): 27 | """ 28 | CMAQ reaction form 5 29 | 30 | K1 = CMAQ_1to4(A0, B0, C0) 31 | 32 | Returns Kf / K1 33 | """ 34 | # REAL A0, B0, C0 35 | # REAL(kind=dp) K1, Kf 36 | K1 = CMAQ_1to4(A0, B0, C0) 37 | return Kf / K1 38 | 39 | 40 | def CMAQ_6(A0, B0, C0, Kf): 41 | """ 42 | CMAQ reaction form 6 43 | 44 | K1 = CMAQ_1to4(A0, B0, C0) 45 | 46 | Returns Kf * K1 47 | """ 48 | # REAL A0, B0, C0 49 | # REAL(kind=dp) K1, Kf 50 | K1 = CMAQ_1to4(A0, B0, C0) 51 | return Kf * K1 52 | 53 | 54 | def CMAQ_7(A0, B0, C0): 55 | """ 56 | CMAQ reaction form 6 57 | 58 | K0 = CMAQ_1to4(A0, B0, C0) 59 | 60 | Returns K0 * (1 + .6 * PRESS / 101325.) # Pressure is in Pascals 61 | """ 62 | # REAL A0, B0, C0 63 | # REAL(kind=dp) K0 64 | K0 = CMAQ_1to4(A0, B0, C0) 65 | return K0 * (1 + .6 * P / 101325.) # Pressure is in Pascals 66 | 67 | 68 | def CMAQ_8(A0, C0, A2, C2, A3, C3): 69 | """ 70 | CMAQ reaction form 8 71 | 72 | K0 = (A0) * exp(-(C0) / TEMP) 73 | K2 = (A2) * exp(-(C2) / TEMP) 74 | K3 = (A3) * exp(-(C3) / TEMP) 75 | K3 = K3 * M 76 | 77 | Returns K0 + K3 / (1.0 + K3 / K2 ) 78 | """ 79 | # REAL A0, C0, A2, C2, A3, C3 80 | # REAL(kind=dp) K0, K2, K3 81 | K0 = (A0) * exp(-(C0) / TEMP) 82 | K2 = (A2) * exp(-(C2) / TEMP) 83 | K3 = (A3) * exp(-(C3) / TEMP) 84 | K3 = K3 * M 85 | return K0 + K3 / (1.0 + K3 / K2) 86 | 87 | 88 | def CMAQ_9(A1, C1, A2, C2): 89 | """ 90 | CMAQ reaction rate form 9 91 | 92 | K1 = (A1) * exp(-(C1) / TEMP) 93 | K2 = (A2) * exp(-(C2) / TEMP) 94 | 95 | M = third body concentration (molecules/cm3) and must be 96 | defined in the stdfuncs namespace 97 | 98 | Returns K1 + K2 * M 99 | """ 100 | # REAL(kind=dp) A1, C1, A2, C2 101 | # REAL(kind=dp) K1, K2 102 | K1 = (A1) * exp(-(C1) / TEMP) 103 | K2 = (A2) * exp(-(C2) / TEMP) 104 | return K1 + K2 * M 105 | 106 | 107 | def CMAQ_10(A0, B0, C0, A1, B1, C1, CF, N): 108 | """ 109 | CMAQ reaction rate form 10 110 | 111 | K0 = CMAQ_1to4(A0, B0, C0) 112 | K1 = CMAQ_1to4(A1, B1, C1) 113 | K0 = K0 * M 114 | K1 = K0 / K1 115 | 116 | M = third body concentration (molecules/cm3) and must be 117 | defined in the stdfuncs namespace 118 | 119 | Returns (K0 / (1.0 + K1))* \ 120 | (CF)**(1.0 / (1.0 / (N) + (log10(K1))**2)) 121 | """ 122 | # REAL A0, B0, C0, A1, B1, C1, CF, N 123 | # REAL(kind=dp) K0, K1 124 | K0 = CMAQ_1to4(A0, B0, C0) 125 | K1 = CMAQ_1to4(A1, B1, C1) 126 | K0 = K0 * M 127 | K1 = K0 / K1 128 | return (K0 / (1.0 + K1)) * \ 129 | (CF)**(1.0 / (1.0 / (N) + (log10(K1))**2)) 130 | 131 | 132 | def CMAQ_10D(A0, B0, C0, A1, B1, C1, CF, N): 133 | """ 134 | Same as reaction rate form 10, but implemented 135 | to provide compatibility for fortran code 136 | that need a DOUBLE form 137 | """ 138 | return CMAQ_10(A0, B0, C0, A1, B1, C1, CF, N) 139 | 140 | 141 | def OH_CO(A0, B0, C0, A1, B1, C1, CF, N): 142 | """ 143 | OH + CO reaction rate 144 | 145 | *Note: Mostly like CMAQ_10, but slight difference in K1 146 | 147 | K0 = CMAQ_1to4(A0, B0, C0) 148 | K1 = CMAQ_1to4(A1, B1, C1) 149 | K0 = K0 150 | K1 = K0 / (K1 / M) 151 | 152 | M = third body concentration (molecules/cm3) and must be 153 | defined in the stdfuncs namespace 154 | 155 | return (K0 / (1.0 + K1))* \ 156 | (CF)**(1.0 / (1.0 / (N) + (log10(K1))**2)) 157 | 158 | """ 159 | # REAL A0, B0, C0, A1, B1, C1, CF, N 160 | # REAL(kind=dp) K0, K1 161 | K0 = CMAQ_1to4(A0, B0, C0) 162 | K1 = CMAQ_1to4(A1, B1, C1) 163 | K0 = K0 164 | K1 = K0 / (K1 / M) 165 | return (K0 / (1.0 + K1)) * \ 166 | (CF)**(1.0 / (1.0 / (N) + (log10(K1))**2)) 167 | -------------------------------------------------------------------------------- /pykpp/funcs/geoschem.py: -------------------------------------------------------------------------------- 1 | __all__ = [ 2 | 'GEOS_STD', 'GEOS_P', 'GEOS_Z', 'GEOS_Y', 'GEOS_X', 'GEOS_C', 'GEOS_K', 3 | 'GEOS_HR', 'GEOS_V', 'GEOS_E', 'FYRNO3', 'JHNO4_NEAR_IR', 'GEOS_KHO2', 4 | 'GEOS_A', 'GEOS_B', 'GEOS_JO3', 'GEOS_G', 'GEOS_T', 'GEOS_F', 'GEOS_L', 5 | 'GEOS_O', 'GEOS_N', 'GEOS_Q', 'GEOS_JO3', 'GEOS_JO3_2', 'update_func_world' 6 | ] 7 | from warnings import warn 8 | from numpy import exp, log10, float64, sqrt 9 | 10 | DBLE = float64 11 | N2 = O2 = M = TEMP = INVTEMP = 0 12 | int_HO2 = C = H2O = H2 = 0 13 | 14 | 15 | def update_func_world(mech, world): 16 | """ 17 | Function to update globals for user defined functions 18 | """ 19 | global INVTEMP, T300I 20 | globals().update(world) 21 | INVTEMP = 1 / world['TEMP'] 22 | T300I = 300. / world['TEMP'] 23 | 24 | 25 | def GEOS_STD(A0, B0, C0): 26 | """ 27 | GEOS-Chem standard reaction rate with 28 | the form K = A * (300 / T)**B * EXP(C / T) 29 | 30 | Returns A0 * (300. / TEMP)**B0 * exp(C0 / TEMP) 31 | """ 32 | global INVTEMP 33 | # REAL A0, B0, C0 34 | 35 | return A0 * (T300I)**B0 * exp(C0 * INVTEMP) 36 | 37 | 38 | def GEOS_P(A0, B0, C0, A1, B1, C1, 39 | FCV, FCT1, FCT2): 40 | """ 41 | GEOS-Chem pressure dependent TROE falloff equation 42 | 43 | if (FCT2 != 0.000000e+00): 44 | CF = exp(-TEMP / FCT1) + exp(-FCT2 / TEMP) 45 | elif (FCT1 != 0.000000e+00): 46 | CF = exp(-TEMP / FCT1) 47 | else: 48 | CF = FCV 49 | 50 | K0M = GEOS_STD(A0, B0, C0) * M 51 | 52 | K1 = GEOS_STD(A1, B1, C1) 53 | K1 = K0M / K1 54 | 55 | return (K0M / (1.0 + K1))* \ 56 | (CF)**(1.0 / (1.0 + (log10(K1))**2)) 57 | 58 | """ 59 | 60 | # REAL A0, B0, C0, A1, B1, C1 ,CF 61 | # REAL FCV, FCT1, FCT2 62 | # REAL(kind=dp) K0M, K1 63 | 64 | if (FCT2 != 0.000000e+00): 65 | CF = exp(-TEMP / FCT1) + exp(-FCT2 * INVTEMP) 66 | elif (FCT1 != 0.000000e+00): 67 | CF = exp(-TEMP / FCT1) 68 | else: 69 | CF = FCV 70 | 71 | K0M = GEOS_STD(A0, B0, C0) * M 72 | 73 | K1 = GEOS_STD(A1, B1, C1) 74 | K1 = K0M / K1 75 | 76 | return (K0M / (1.0 + K1)) * \ 77 | (CF)**(1.0 / (1.0 + (log10(K1))**2)) 78 | 79 | 80 | def GEOS_Z(A0, B0, C0, A1, B1, C1, A2, B2, C2): 81 | """ 82 | GEOS-Chem Z reaction rate form 83 | 84 | K0 = GEOS_STD(A0, B0, C0) 85 | K1 = GEOS_STD(A1, B1, C1)*M 86 | K2 = GEOS_STD(A2, B2, C2) 87 | 88 | Returns (K0 + K1) * (1 + H2O * K2) 89 | """ 90 | # REAL A0, B0, C0, A1, B1, C1, A2, B2, C2 91 | # REAL(kind=dp) K0, K1, K2 92 | 93 | K0 = GEOS_STD(A0, B0, C0) 94 | K1 = GEOS_STD(A1, B1, C1)*M 95 | K2 = GEOS_STD(A2, B2, C2) 96 | 97 | return (K0 + K1) * (1 + H2O * K2) 98 | 99 | 100 | def GEOS_Y(A0, B0, C0): 101 | """ 102 | GEOS-Chem reaction form Y 103 | 104 | A0, B0, and C0 are numeric inputs that are ignored 105 | IGNORES INPUTS per v08-02-04 update 106 | """ 107 | # REAL A0, B0, C0 108 | # REAL(kind=dp) K0 109 | # REAL(kind=dp) KHI1,KLO1,XYRAT1,BLOG1,FEXP1,KHI2,KLO2,XYRAT2,BLOG2 110 | # REAL(kind=dp) FEXP2,KCO1,KCO2,KCO 111 | 112 | # IGNORES INPUTS per v08-02-04 update 113 | # K0 = GEOS_STD(A0, B0, C0) 114 | # GEOS_Y = K0 * (1 + .6 * (PRESS * 100.) / 101325.) 115 | KLO1 = 5.9e-33 * (T300I)**(1.4e0) 116 | KHI1 = 1.1e-12 * (T300I)**(-1.3e0) 117 | XYRAT1 = KLO1 * M / KHI1 118 | BLOG1 = log10(XYRAT1) 119 | FEXP1 = 1.e0 / (1.e0 + BLOG1 * BLOG1) 120 | KCO1 = KLO1 * M * 0.6**FEXP1 / (1.e0 + XYRAT1) 121 | KLO2 = 1.5e-13 * (T300I)**(-0.6e0) 122 | KHI2 = 2.1e09 * (T300I)**(-6.1e0) 123 | XYRAT2 = KLO2 * M / KHI2 124 | BLOG2 = log10(XYRAT2) 125 | FEXP2 = 1.e0 / (1.e0 + BLOG2 * BLOG2) 126 | KCO2 = KLO2 * 0.6**FEXP2 / (1.e0 + XYRAT2) 127 | return KCO1 + KCO2 128 | 129 | 130 | def GEOS_X(A0, B0, C0, A1, B1, C1, A2, B2, C2): 131 | """ 132 | GEOS-Chem reaction rate form Z 133 | 134 | K0 = GEOS_STD(A0, B0, C0) 135 | K2 = GEOS_STD(A1, B1, C1) 136 | K3 = GEOS_STD(A2, B2, C2) 137 | K3 = K3 * M 138 | 139 | Returns K0 + K3 / (1.0 + K3 / K2 ) 140 | """ 141 | # REAL A0, B0, C0, A1, B1, C1, A2, B2, C2 142 | # REAL(kind=dp) K0, K2, K3 143 | K0 = GEOS_STD(A0, B0, C0) 144 | K2 = GEOS_STD(A1, B1, C1) 145 | K3 = GEOS_STD(A2, B2, C2) 146 | K3 = K3 * M 147 | return K0 + K3 / (1.0 + K3 / K2) 148 | 149 | 150 | def GEOS_C(A0, B0, C0): 151 | """ 152 | GEOS-Chem reaction form C 153 | 154 | K1 = GEOS_STD(A0, B0, C0) 155 | 156 | Returns K1 * (O2 + 3.5e18) / (2.0 * O2 + 3.5e18) 157 | """ 158 | # REAL A0, B0, C0, A1, B1, C1, A2, B2, C2 159 | # REAL(kind=dp) K1 160 | K1 = GEOS_STD(A0, B0, C0) 161 | return K1 * (O2 + 3.5e18) / (2.0 * O2 + 3.5e18) 162 | 163 | 164 | def GEOS_K(A0, B0, C0): 165 | """ 166 | GEOS-Chem reaction form K 167 | 168 | ** Not implemented returns 0. 169 | """ 170 | warn("GEOS_K not implemented, returning 0") 171 | # REAL A0, B0, C0 172 | return 0 173 | 174 | 175 | def GEOS_HR(A0, B0, C0, A1, B1, C1): 176 | """ 177 | GEOS-Chem reaction form HR 178 | 179 | ** Not implemented returns 0. 180 | """ 181 | # REAL A0, B0, C0 182 | XCARBN = A1 183 | return GEOS_STD(A0, B0, C0) * (1e+0 - exp(-0.245e+0 * XCARBN)) 184 | 185 | 186 | def GEOS_N(A0, B0, C0): 187 | """ 188 | GEOS-Chem reaction form N 189 | 190 | ** Not implemented returns 0. 191 | """ 192 | ONE_SEVENTYTHREE = 1e+0 / 73e+0 193 | GLYC_FRAC = 1e+0 - 11.0729e+0 * exp(-ONE_SEVENTYTHREE * TEMP) 194 | if GLYC_FRAC < 0: 195 | GLYC_FRAC = 0 196 | # REAL A0, B0, C0 197 | return GEOS_STD(A0, B0, C0) * GLYC_FRAC 198 | 199 | 200 | def GEOS_O(A0, B0, C0): 201 | """ 202 | GEOS-Chem reaction form O 203 | 204 | ** Not implemented returns 0. 205 | """ 206 | ONE_SEVENTYTHREE = 1e+0 / 73e+0 207 | GLYC_FRAC = 1e+0 - 11.0729e+0 * exp(-ONE_SEVENTYTHREE * TEMP) 208 | if GLYC_FRAC < 0: 209 | GLYC_FRAC = 0 210 | # REAL A0, B0, C0 211 | return GEOS_STD(A0, B0, C0) * (1 - GLYC_FRAC) 212 | 213 | 214 | def GEOS_Q(A0): 215 | """ 216 | GEOS-Chem reaction form Q 217 | 218 | ** Not implemented returns 0. 219 | """ 220 | RO1DplH2O = 1.63e-10 * exp(60. / TEMP) * H2O 221 | RO1DplH2 = 1.2e-10 * H2 222 | RO1DplN2 = 2.15e-11 * exp(110. / TEMP) * N2 223 | RO1DplO2 = 3.30e-11 * exp(55. / TEMP) * O2 224 | RO1D = RO1DplH2O + RO1DplH2 + RO1DplN2 + RO1DplO2 225 | return A0 * RO1DplH2O / RO1D 226 | 227 | 228 | def GEOS_F(A0, B0, C0): 229 | ONE_SIXTY = 1. / 60. 230 | HAC_FRAC = 1e+0 - 23.7e+0 * exp(-ONE_SIXTY * TEMP) 231 | if HAC_FRAC < 0e+0: 232 | HAC_FRAC = 0e+0 233 | return GEOS_STD(A0, B0, C0) * HAC_FRAC 234 | 235 | 236 | def GEOS_L(A0, B0, C0): 237 | ONE_SIXTY = 1. / 60. 238 | HAC_FRAC = 1e+0 - 23.7e+0 * exp(-ONE_SIXTY * TEMP) 239 | if HAC_FRAC < 0e+0: 240 | HAC_FRAC = 0e+0 241 | return GEOS_STD(A0, B0, C0) * (1 - HAC_FRAC) 242 | 243 | 244 | def GEOS_T(A0, B0, C0): 245 | """ 246 | GEOS-Chem reaction form T 247 | 248 | ** Not implemented returns 0. 249 | """ 250 | warn("GEOS_T assumed active.") 251 | # REAL A0, B0, C0 252 | return GEOS_STD(A0, B0, C0) 253 | 254 | 255 | def GEOS_V(A0, B0, C0, A1, B1, C1): 256 | """ 257 | GEOS-Chem reaction form V 258 | 259 | K1 = GEOS_STD(A0, B0, C0) 260 | K2 = GEOS_STD(A1, B1, C1) 261 | return K1 / (1 + K2) 262 | """ 263 | # REAL A0, B0, C0, A1, B1, C1 264 | # REAL(kind=dp) K1, K2 265 | K1 = GEOS_STD(A0, B0, C0) 266 | K2 = GEOS_STD(A1, B1, C1) 267 | return K1 / (1 + K2) 268 | 269 | 270 | def GEOS_E(A0, B0, C0, Kf): 271 | """ 272 | GEOS-Chem reaction form E 273 | 274 | K1 = GEOS_STD(A0, B0, C0) 275 | 276 | Returns Kf / K1 277 | """ 278 | 279 | # REAL A0, B0, C0 280 | # REAL(kind=dp) K1, Kf 281 | K1 = GEOS_STD(A0, B0, C0) 282 | return Kf / K1 283 | 284 | 285 | def FYRNO3(CN): 286 | """ 287 | GEOS-Chem equation FYRNO3 implemented based on GEOS-Chem 288 | version 9 289 | """ 290 | 291 | Y300 = .826 292 | ALPHA = 1.94E-22 293 | BETA = .97 294 | XM0 = 0. 295 | XMINF = 8.1 296 | XF = .411 297 | 298 | # REAL*4 CN 299 | # REAL*4 XCARBN, ZDNUM, TT, XXYN, YYYN, AAA, ZZYN, RARB 300 | XCARBN = CN 301 | ZDNUM = M 302 | TT = TEMP 303 | 304 | # XXYN = ALPHA * exp(BETA * XCARBN) * ZDNUM * ((300. / TT)**XM0) 305 | # YYYN = Y300 * ((300. / TT)**XMINF) 306 | XXYN = ALPHA * exp(BETA * XCARBN) * ZDNUM * ((T300I)**XM0) 307 | YYYN = Y300 * ((T300I)**XMINF) 308 | AAA = log10(XXYN / YYYN) 309 | ZZYN = 1. / (1. + AAA / AAA) 310 | RARB = (XXYN / (1. + (XXYN / YYYN))) * (XF**ZZYN) 311 | return RARB / (1. + RARB) 312 | 313 | 314 | def JHNO4_NEAR_IR(HNO4J): 315 | """ 316 | Adding 1e-5 (1/s) to HNO4 photolysis to 317 | account for near IR 318 | """ 319 | if (HNO4J > 0.e0): 320 | return HNO4J + 1e-5 321 | else: 322 | return HNO4J 323 | 324 | 325 | def GEOS_KHO2(A0, B0, C0): 326 | """ 327 | Implemented KHO2 based on GEOS-Chem version 9 328 | """ 329 | STKCF = HO2(SLF_RAD, TEMP, M, sqrt(DBLE(A0)), C(ind_HO2), 8, 0) 330 | GEOS_KHO2 = ARSL1K(SLF_AREA, SLF_RAD, M, STKCF, sqrt(TEMP), sqrt((A0))) 331 | STKCF = HO2(EPOM_RAD, TEMP, M, sqrt((A0)), C(ind_HO2), 10, 0) 332 | out = ( 333 | GEOS_KHO2 + ARSL1K( 334 | EPOM_AREA, EPOM_RAD, M, STKCF, sqrt(TEMP), sqrt(DBLE(A0)) 335 | ) 336 | ) 337 | return out 338 | 339 | 340 | def GEOS_A(A0, B0, C0, A1, B1, C1): 341 | """ 342 | GEOS-Chem reaction form A 343 | 344 | TMP_A0 = A0 * FYRNO3(A1) 345 | 346 | Returns GEOS_STD(TMP_A0, B0, C0) 347 | """ 348 | # REAL A0, B0, C0, A1, B1, C1 349 | # REAL TMP_A0 350 | TMP_A0 = A0 * FYRNO3(A1) 351 | return GEOS_STD(TMP_A0, B0, C0) 352 | 353 | 354 | def GEOS_B(A0, B0, C0, A1, B1, C1): 355 | """ 356 | GEOS-Chem reaction form B 357 | 358 | TMP_A0 = A0 * ( 1. - FYRNO3(A1) ) 359 | 360 | Returns GEOS_STD(TMP_A0, B0, C0) 361 | """ 362 | # REAL A0, B0, C0, A1, B1, C1 363 | # REAL TMP_A0 364 | TMP_A0 = A0 * (1. - FYRNO3(A1)) 365 | return GEOS_STD(TMP_A0, B0, C0) 366 | 367 | 368 | def GEOS_G(A0, B0, C0, A1, B1, C1): 369 | """ 370 | GEOS-Chem reaction form A 371 | 372 | K1 = GEOS_STD(A0, B0, C0) 373 | K2 = GEOS_STD(A1, B1, C1) 374 | 375 | Returns K1 / ( 1.0 + K1 * O2 ) 376 | """ 377 | # REAL A0, B0, C0, A1, B1, C1 378 | # REAL(kind=dp) K1, K2 379 | K1 = GEOS_STD(A0, B0, C0) 380 | K2 = GEOS_STD(A1, B1, C1) 381 | return K1 / (1.0 + K1 * O2) 382 | 383 | 384 | def GEOS_JO3(O3J): 385 | """ 386 | GEOS-Chem reaction form ozone photolysis 387 | 388 | T3I = 1.0/TEMP 389 | Returs O3J * \ 390 | 1.45e-10 * exp( 89.0 * T3I) * H2O / \ 391 | ( 1.45e-10 * exp( 89.0 * T3I) * H2O + \ 392 | 2.14e-11 * exp(110.0 * T3I) * N2 + \ 393 | 3.20e-11 * exp( 70.0 * T3I) * O2 \ 394 | ) 395 | """ 396 | 397 | # REAL(kind=dp) O3J, T3I 398 | T3I = 1.0*INVTEMP 399 | fh2o = ( 400 | 1.45e-10 * exp(89.0 * T3I) * H2O 401 | / ( 402 | 1.45e-10 * exp(89.0 * T3I) * H2O 403 | + 2.14e-11 * exp(110.0 * T3I) * N2 404 | + 3.20e-11 * exp(70.0 * T3I) * O2 405 | ) 406 | ) 407 | return O3J * fh2o 408 | 409 | 410 | def GEOS_JO3_2(O3J): 411 | T3I = 1.0*INVTEMP 412 | RO1DplH2O = 1.63e-10 * exp(60.0 * T3I) * H2O 413 | RO1DplH2 = 1.2e-10 * H2 414 | RO1DplN2 = 2.15e-11 * exp(110.0 * T3I) * N2 415 | RO1DplO2 = 3.30e-11 * exp(55.0 * T3I) * O2 416 | RO1D = RO1DplH2O + RO1DplH2 + RO1DplN2 + RO1DplO2 417 | return O3J * RO1DplH2 / RO1D 418 | -------------------------------------------------------------------------------- /pykpp/funcs/kpp.py: -------------------------------------------------------------------------------- 1 | __all__ = ['ARR', 'ARR2', 'EP2', 'EP3', 'FALL', 'DP3', 'k_3rd', 'k_arr'] 2 | from numpy import exp, log10 3 | 4 | TEMP = M = 0 5 | 6 | 7 | def update_func_world(mech, world): 8 | """ 9 | Function to update globals for user defined functions 10 | """ 11 | globals().update(world) 12 | 13 | 14 | def ARR(A0, B0, C0): 15 | """ 16 | A0, B0 and C0 - numeric values used to 17 | calculate a reaction rate (1/s) based on 18 | the Arrhenius equation in the following 19 | form: 20 | 21 | A0 * exp(-B0/TEMP) * (TEMP / 300.)**(C0) 22 | 23 | Returns a rate in per time 24 | """ 25 | out = A0 * exp(-B0 / TEMP) * (TEMP / 300.0)**(C0) 26 | return out 27 | 28 | 29 | def ARR2(A0, B0): 30 | """ 31 | A0 and B0 - numeric values used to 32 | calculate a reaction rate (1/s) based on 33 | the Arrhenius equation in the following 34 | form: 35 | 36 | A0 * exp(B0/TEMP) 37 | 38 | Returns a rate in per time 39 | 40 | Note: ARR2 sign of B0 is different than ARR 41 | """ 42 | out = A0 * exp(B0 / TEMP) 43 | return out 44 | 45 | 46 | def EP2(A0, C0, A2, C2, A3, C3): 47 | """ 48 | A0, C0, A2, C2, A3, and C3 - numeric 49 | values used to calculate 3 rates (K0, K2, 50 | and K3), each of the form A * exp(-C / TEMP), 51 | to return a rate of the following form: 52 | 53 | K0 + K3 * M * 1e6 / (1. + K3 * M * 1e6 / K2) 54 | 55 | Returns a rate in per time 56 | """ 57 | K0 = A0 * exp(-C0 / TEMP) 58 | K2 = A2 * exp(-C2 / TEMP) 59 | K3 = A3 * exp(-C3 / TEMP) 60 | K3 = K3 * M * 1.0E6 61 | return K0 + K3 / (1.0 + K3 / K2) 62 | 63 | 64 | def EP3(A1, C1, A2, C2): 65 | """ 66 | A1, C1, A2, and C2 - numeric 67 | values used to calculate 3 rates (K0, K2, 68 | and K3), each of the form A * exp(-C / TEMP), 69 | to return a rate of the following form: 70 | 71 | K1 + K2 * M * 1e6 72 | 73 | Returns a rate in per time 74 | """ 75 | K1 = A1 * exp(-C1 / TEMP) 76 | K2 = A2 * exp(-C2 / TEMP) 77 | return K1 + K2 * (1.0E6 * M) 78 | 79 | 80 | def FALL(A0, B0, C0, A1, B1, C1, CF): 81 | """ 82 | Troe fall off equation 83 | 84 | A0, B0, C0, A1, B1, C1 - numeric values 85 | to calculate 2 reaction rates (K0, K1) using ARR 86 | function; returns a rate in the following form 87 | 88 | K0M = K0 * M * 1e6 89 | KR = K0 / K1 90 | 91 | Returns (K0M / (1.0 + KR))* CF**(1.0 / (1.0 + (log10(KR))**2)) 92 | """ 93 | 94 | K0 = ARR(A0, B0, C0) 95 | K1 = ARR(A1, B1, C1) 96 | K0 = K0 * M * 1.0E6 97 | K1 = K0 / K1 98 | return (K0 / (1.0 + K1)) * CF**(1.0 / (1.0 + (log10(K1))**2)) 99 | 100 | 101 | def k_3rd(temp, cair, k0_300K, n, kinf_300K, m, fc): 102 | """ 103 | """ 104 | zt_help = 300. / temp 105 | k0_T = k0_300K * zt_help**(n) * cair # k_0 at current T 106 | kinf_T = kinf_300K * zt_help**(m) # k_inf at current T 107 | k_ratio = k0_T / kinf_T 108 | return k0_T / (1. + k_ratio) * fc**(1. / (1. + log10(k_ratio)**2)) 109 | 110 | 111 | DP3 = EP3 112 | 113 | 114 | def k_arr(k_298, tdep, temp): 115 | """ 116 | """ 117 | return k_298 * exp(tdep * (1. / temp - 3.3540E-3)) # 1/298.15=3.3540e-3 118 | -------------------------------------------------------------------------------- /pykpp/funcs/mcm.py: -------------------------------------------------------------------------------- 1 | __all__ = ['MCMJ'] 2 | from warnings import warn 3 | from pykpp.tuv.tuv5pt0 import TUV_J 4 | 5 | 6 | def MCMJ(idx, THETA): 7 | """ 8 | Parameters: 9 | idx - index from MCM export (v3.3.1) 10 | THETA - zenith angle in degrees 11 | 12 | Returns: 13 | photolysis frequency (s**-1) from TUV_J 14 | for closest surrogate. 15 | """ 16 | if idx == 1: 17 | """ 18 | MCM 19 | 1 = O3 -> O(1D) +O2 20 | TUV v5.0 21 | 2 = O3 -> O2 + O(1D) 22 | """ 23 | return TUV_J(2, THETA) 24 | elif idx == 2: 25 | """ 26 | MCM 27 | 2 = O3 - O(3P) +O2 28 | TUV v5.0 29 | 3 = O3 -> O2 + O(3P) 30 | """ 31 | return TUV_J(3, THETA) 32 | elif idx == 3: 33 | """ 34 | MCM 35 | 3 = H2O2 -> OH + OH 36 | TUV v5.0 37 | 5 = H2O2 -> 2 OH 38 | """ 39 | return TUV_J(5, THETA) 40 | elif idx == 4: 41 | """ 42 | MCM 43 | 4 = NO2 -> NO + O(3P) 44 | TUV v5.0 45 | 6 = NO2 -> NO + O(3P) 46 | """ 47 | return TUV_J(6, THETA) 48 | elif idx == 5: 49 | """ 50 | MCM 51 | 5 = NO3 -> NO + O2 52 | TUV v5.0 53 | 7 = NO3 -> NO + O2 54 | """ 55 | return TUV_J(7, THETA) 56 | elif idx == 6: 57 | """ 58 | MCM 59 | 6 = NO3 -> NO2 + O(3P) 60 | TUV v5.0 61 | 8 = NO3 -> NO2 + O(3P) 62 | """ 63 | return TUV_J(8, THETA) 64 | elif idx == 7: 65 | """ 66 | MCM 67 | 7 = HONO -> NO + OH 68 | TUV v5.0 69 | 12 = HNO2 -> OH + NO 70 | """ 71 | return TUV_J(12, THETA) 72 | elif idx == 8: 73 | """ 74 | MCM 75 | 8 = HNO3 -> NO2 + OH 76 | TUV v5.0 77 | 13 = HNO3 -> OH + NO2 78 | """ 79 | return TUV_J(13, THETA) 80 | elif idx == 11: 81 | """ 82 | MCM 83 | 11 = HCHO -> H + HCO 84 | TUV v5.0 85 | 17 = CH2O -> H + HCO 86 | """ 87 | return TUV_J(17, THETA) 88 | elif idx == 12: 89 | """ 90 | MCM 91 | 12 = HCHO -> H2 + CO 92 | TUV v5.0 93 | 18 = CH2O -> H2 + CO 94 | """ 95 | return TUV_J(18, THETA) 96 | elif idx == 13: 97 | """ 98 | MCM 99 | 13 = CH3CHO -> CH3 + HCO 100 | TUV v5.0 101 | 19 = CH3CHO -> CH3 + HCO 102 | 20 = CH3CHO -> CH4 + CO 103 | 21 = CH3CHO -> CH3CO + H 104 | 105 | Notes: Assuming the MCM uses same total QY with only one product set. 106 | """ 107 | return TUV_J(19, THETA)+TUV_J(20, THETA)+TUV_J(21, THETA) 108 | elif idx == 14: 109 | """ 110 | MCM 111 | 14 = C2H5CHO -> C2H5 + HCO 112 | TUV v5.0 113 | 22 = C2H5CHO -> C2H5 + HCO 114 | """ 115 | return TUV_J(22, THETA) 116 | elif idx == 15: 117 | """ 118 | MCM 119 | 15 = C3H7CHO -> n-C3H7 + HCO (QY: 0.21) 120 | 16 = C3H7CHO -> C2H4 + CH3CHO (QY: 0.1) 121 | TUV v5.0 122 | 22 = C2H5CHO -> C2H5 + HCO 123 | 124 | Notes: Using C3 aldehyde as a surrogate. 125 | """ 126 | return .667*TUV_J(22, THETA) 127 | elif idx == 16: 128 | """ 129 | MCM 130 | 15 = C3H7CHO -> n-C3H7 + HCO (QY: 0.21) 131 | 16 = C3H7CHO -> C2H4 + CH3CHO (QY: 0.1) 132 | TUV v5.0 133 | 22 = C2H5CHO -> C2H5 + HCO 134 | 135 | Notes: Using C3 aldehyde as a surrogate. 136 | """ 137 | return .333*TUV_J(22, THETA) 138 | elif idx == 17: 139 | """ 140 | MCM 141 | 17 = IPRCHO -> n-C4H9 + HCO 142 | TUV v5.0 143 | 22 = C2H5CHO -> C2H5 + HCO 144 | 145 | Notes: Using C3 aldehyde as a surrogate. 146 | """ 147 | return TUV_J(22, THETA) 148 | elif idx == 18: 149 | """ 150 | MCM 151 | 18 = MACR -> CH2=CCH3 + HCO (QY = 1.9e-3) 152 | 19 = MACR -> CH2=C(CH3)CO + H (QY = 1.9e-3) 153 | TUV v5.0 154 | 25 = CH2=C(CH3)CHO -> Products 155 | 156 | Notes: Assuming equal distribution among QY channels. 157 | """ 158 | return .5*TUV_J(25, THETA) 159 | elif idx == 19: 160 | """ 161 | MCM 162 | 18 = MACR -> CH2=CCH3 + HCO (QY = 1.9e-3) 163 | 19 = MACR -> CH2=C(CH3)CO + H (QY = 1.9e-3) 164 | TUV v5.0 165 | 25 = CH2=C(CH3)CHO -> Products 166 | 167 | Notes: Assuming equal distribution among QY channels. 168 | """ 169 | return .5*TUV_J(25, THETA) 170 | elif idx == 20: 171 | """ 172 | MCM 173 | 20 = C5HPALD1 -> CH3C(CHO)=CHCH2O + OH 174 | TUV v5.0 175 | 25 = CH2=C(CH3)CHO -> Products 176 | 177 | Notes: This compound cleaves at the peroxide, so using CH3OOH as a 178 | surrogate. 179 | """ 180 | return TUV_J(31, THETA) 181 | elif idx == 21: 182 | """ 183 | MCM 184 | 21 = CH3COCH3 -> CH3CO + CH3 185 | TUV v5.0 186 | 26 = CH3COCH3 -> CH3CO + CH3 187 | """ 188 | return TUV_J(26, THETA) 189 | elif idx == 22: 190 | """ 191 | MCM 192 | 22 = MEK -> CH3CO + C2H5 193 | TUV v5.0 194 | 28 = CH3COCH2CH3 -> CH3CO + CH2CH3 195 | """ 196 | return TUV_J(28, THETA) 197 | elif idx == 23: 198 | """ 199 | MCM 200 | 23 = MVK -> CH3CH=CH2 + CO 201 | 24 = MVK -> CH3CO + CH2=CH 202 | TUV v5.0 203 | 27 = CH3COCHCH2 -> Products 204 | 205 | Note: Assuming equal split. 206 | """ 207 | return .5*TUV_J(27, THETA) 208 | elif idx == 24: 209 | """ 210 | MCM 211 | 23 = MVK -> CH3CH=CH2 + CO 212 | 24 = MVK -> CH3CO + CH2=CH 213 | TUV v5.0 214 | 27 = CH3COCHCH2 -> Products 215 | 216 | Note: Assuming equal split. 217 | """ 218 | return .5*TUV_J(27, THETA) 219 | elif idx == 31: 220 | """ 221 | MCM 222 | 31 = GLYOX -> CO + CO + H2 223 | TUV v5.0 224 | unavailable for this channel 225 | """ 226 | return 0. 227 | elif idx == 32: 228 | """ 229 | MCM 230 | 32 = GLYOX -> HCHO + CO 231 | TUV v5.0 232 | 45 = CHOCHO -> CH2O + CO 233 | """ 234 | return TUV_J(45, THETA) 235 | elif idx == 33: 236 | """ 237 | MCM 238 | 33 = GLYOX -> HCO + HCO 239 | TUV v5.0 240 | 44 = CHOCHO -> HCO + HCO 241 | """ 242 | return TUV_J(44, THETA) 243 | elif idx == 34: 244 | """ 245 | MCM 246 | 34 = MGLYOX -> CH3CO + HCO 247 | TUV v5.0 248 | 46 = CH3COCHO -> CH3CO + HCO 249 | """ 250 | return TUV_J(46, THETA) 251 | elif idx == 35: 252 | """ 253 | MCM 254 | 35 = BIACET -> CH3CO + CH3CO 255 | TUV v5.0 256 | 47 = CH3COCOCH3 -> Products 257 | """ 258 | return TUV_J(47, THETA) 259 | elif idx == 41: 260 | """ 261 | MCM 262 | 41 = CH3OOH -> CH3O + OH 263 | TUV v5.0 264 | 31 = CH3OOH -> CH3O + OH 265 | """ 266 | return TUV_J(31, THETA) 267 | elif idx == 51: 268 | """ 269 | MCM 270 | 51 = CH3NO3 -> CH3O + NO2 271 | TUV v5.0 272 | 34 = CH3ONO2 -> CH3O + NO2 273 | """ 274 | return TUV_J(34, THETA) 275 | elif idx == 52: 276 | """ 277 | MCM 278 | 52 = C2H5NO3 -> C2H5O + NO2 279 | TUV v5.0 280 | 35 = CH3CH2ONO2 -> CH3CH2O + NO2 281 | """ 282 | return TUV_J(35, THETA) 283 | elif idx == 53: 284 | """ 285 | MCM 286 | 53 = NC3H7NO3 -> n-C3H7O + NO2 287 | TUV v5.0 288 | 35 = CH3CH2ONO2 -> CH3CH2O + NO2 289 | """ 290 | return TUV_J(35, THETA) 291 | elif idx == 54: 292 | """ 293 | MCM 294 | 54 = IC3H7NO3 -> CH3C(O.)CH3 + NO2 295 | TUV v5.0 296 | 36 = CH3CHONO2CH3 -> CH3CHOCH3 + NO2 297 | """ 298 | return TUV_J(36, THETA) 299 | elif idx == 55: 300 | """ 301 | MCM 302 | 55 = TC4H9NO3 -> t-C4H9O + NO2 303 | TUV v5.0 304 | 39 = C(CH3)3(ONO2) -> C(CH3)3(O.) + NO2 305 | """ 306 | return TUV_J(39, THETA) 307 | elif idx == 56: 308 | """ 309 | MCM 310 | 56 = NOA -> CH3C(O)CH2(O.) + NO2 311 | 56 = NOA -> CH3CO + HCHO + NO2 312 | TUV v5.0 313 | 38 = CH3COCH2(ONO2) -> CH3COCH2(O.) + NO2 314 | """ 315 | return TUV_J(38, THETA) 316 | elif idx == 57: 317 | """ 318 | 57 = existed in v3.1, but has been removed 319 | MCM v3.1 320 | C51NO32CO = PEN2ONE1O + NO2 : J(56) ; 321 | C51NO32CO = C3H7CO3 + HCHO + NO2 : J(57) ; 322 | C52NO31CO = C4CHO2O + NO2 : J(56) ; 323 | C52NO31CO = C3H7CHO + CO + HO2 + NO2 : J(57) ; 324 | NO3CH2CHO = NO2 + HCOCH2O : J(56) ; 325 | NO3CH2CHO = HO2 + CO + HCHO + NO2 : J(57) ; 326 | NOA = CH3CO3 + HCHO + NO2 : J(57) ; 327 | CHOPRNO3 = HO2 + CO + CH3CHO + NO2 : J(57) ; 328 | C51NO324CO = HCHO + CO2C3CO3 + NO2 : J(57) ; 329 | C5NO3CO4OH = HO2CO4C5O + NO2 : J(56) ; 330 | C5NO3CO4OH = IPROPOLO2 + HCHO + CO + NO2 : J(57) ; 331 | C5NO3OAOOH = C5NO3COAO + OH : J(41)+J(56)+J(57) ; 332 | C3NO3COOOH = C3NO3COO + OH : J(41)+J(56)+J(57) ; 333 | 334 | """ 335 | warn('mcm TUV has been updated to v3.3.1 and uses J(56) only') 336 | return 0 337 | elif idx == 61: 338 | """ 339 | MCM 340 | 61 = existed in v3.1, but has been removed 341 | """ 342 | return 0 343 | -------------------------------------------------------------------------------- /pykpp/funcs/mozart4.py: -------------------------------------------------------------------------------- 1 | __all__ = [ 2 | 'MZ4_TROE', 'MZ4_USR1', 'MZ4_USR2', 'MZ4_USR3', 'MZ4_USR4', 'MZ4_USR5', 3 | 'MZ4_USR6', 'MZ4_USR7', 'MZ4_USR8', 'MZ4_USR9', 'MZ4_USR10', 'MZ4_USR11', 4 | 'MZ4_USR12', 'MZ4_USR14', 'MZ4_USR21', 'MZ4_USR22', 'MZ4_USR23', 5 | 'MZ4_USR24' 6 | ] 7 | from numpy import exp, log10 8 | 9 | H2O = M = TEMP = 0 10 | 11 | 12 | def update_func_world(mech, world): 13 | """ 14 | Function to update globals for user defined functions 15 | """ 16 | globals().update(world) 17 | 18 | 19 | def MZ4_TROE(A0, B0, A1, B1, factor): 20 | """ 21 | Troe fall off equation as calculated in 22 | MOZART4 23 | """ 24 | # REAL(kind=dp) A0, B0, factor, A1, B1 25 | # REAL(kind=dp) ko, kinf, xpo 26 | ko = A0 * (300.e0 / TEMP)**B0 27 | kinf = A1 * (300.e0 / TEMP)**B1 28 | xpo = ko * M / kinf 29 | MZ4_TROE_TMP = ko / (1. + xpo) 30 | xpo = log10(xpo) 31 | xpo = 1. / (1. + xpo*xpo) 32 | return MZ4_TROE_TMP * factor**xpo 33 | 34 | 35 | def MZ4_USR1(): 36 | """ 37 | USR1 reaction rate as defined in MOZART4 38 | 39 | Returns 6.e-34 * (300.e0/TEMP)**2.4 40 | """ 41 | return 6.e-34 * (300.e0/TEMP)**2.4 42 | 43 | 44 | def MZ4_USR2(): 45 | """ 46 | USR2 reaction rate as defined in MOZART4 47 | 48 | Returns MZ4_TROE(8.5e-29, 6.5e0, 1.1e-11, 1.e0, .6e0) 49 | """ 50 | return MZ4_TROE(8.5e-29, 6.5e0, 1.1e-11, 1.e0, .6e0) 51 | 52 | 53 | def MZ4_USR3(): 54 | """ 55 | USR3 reaction rate as defined in MOZART4 56 | 57 | Returns MZ4_USR2() * 3.333e26 * exp( -10990.e0/TEMP ) 58 | """ 59 | return MZ4_USR2() * 3.333e26 * exp(-10990.e0/TEMP) 60 | 61 | 62 | def MZ4_USR4(): 63 | """ 64 | USR4 reaction rate as defined in MOZART4 65 | 66 | Returns MZ4_TROE(2.0e-30, 3.0e0, 2.5e-11, 0.e0, .6e0) 67 | """ 68 | return MZ4_TROE(2.0e-30, 3.0e0, 2.5e-11, 0.e0, .6e0) 69 | 70 | 71 | def MZ4_USR5(): 72 | """ 73 | USR5 reaction rate as defined in MOZART4 74 | 75 | TINV = 1/TEMP 76 | ko = M * 6.5e-34 * exp( 1335.*tinv ) 77 | ko = ko / (1. + ko/(2.7e-17*exp( 2199.*tinv ))) 78 | 79 | Returns ko + 2.4e-14*exp( 460.*tinv ) 80 | 81 | """ 82 | # REAL(kind=dp) KO, TINV 83 | 84 | tinv = 1/TEMP 85 | ko = M * 6.5e-34 * exp(1335.*tinv) 86 | ko = ko / (1. + ko/(2.7e-17*exp(2199.*tinv))) 87 | return ko + 2.4e-14*exp(460.*tinv) 88 | 89 | 90 | def MZ4_USR6(): 91 | """ 92 | USR6 reaction rate as defined in MOZART4 93 | 94 | Returns MZ4_TROE(1.8e-31, 3.2e0, 4.7e-12, 1.4e0, .6e0) 95 | """ 96 | return MZ4_TROE(1.8e-31, 3.2e0, 4.7e-12, 1.4e0, .6e0) 97 | 98 | 99 | def MZ4_USR7(): 100 | """ 101 | USR7 reaction rate as defined in MOZART4 102 | 103 | Returns MZ4_USR6() * exp( -10900./TEMP )/ 2.1e-27 104 | """ 105 | return MZ4_USR6() * exp(-10900./TEMP) / 2.1e-27 106 | 107 | 108 | def MZ4_USR8(): 109 | """ 110 | USR8 reaction rate as defined in MOZART4 111 | 112 | Returns 1.5e-13 * (1. + 6.e-7 * boltz * M * TEMP) 113 | """ 114 | # real, parameter :: boltz = 1.38044e-16 ! erg/k 115 | return 1.5e-13 * (1. + 6.e-7 * 1.38044e-16 * M * TEMP) 116 | 117 | 118 | def MZ4_USR9(): 119 | """ 120 | USR9 reaction rate as defined in MOZART4 121 | 122 | REAL(kind = dp) ko, kinf, fc, tinv 123 | tinv = 1.0/TEMP 124 | ko = 2.3e-13 * exp( 600.0*tinv ) 125 | kinf = 1.7e-33 * M * exp( 1000.0*tinv ) 126 | fc = 1.0 + 1.4e-21 * H2O * exp( 2200.0*tinv ) 127 | 128 | Returns (ko + kinf) * fc 129 | """ 130 | # REAL(kind = dp) ko, kinf, fc, tinv 131 | tinv = 1.0/TEMP 132 | ko = 2.3e-13 * exp(600.0*tinv) 133 | kinf = 1.7e-33 * M * exp(1000.0*tinv) 134 | fc = 1.0 + 1.4e-21 * H2O * exp(2200.0*tinv) 135 | 136 | return (ko + kinf) * fc 137 | 138 | 139 | def MZ4_USR10(): 140 | """ 141 | USR10 reaction rate as defined in MOZART4 142 | 143 | Returns MZ4_TROE(8.e-27, 3.5e0, 3.e-11, 0e0, .5e0) 144 | """ 145 | return MZ4_TROE(8.e-27, 3.5e0, 3.e-11, 0e0, .5e0) 146 | 147 | 148 | def MZ4_USR11(): 149 | """ 150 | USR11 reaction rate as defined in MOZART4 151 | 152 | Returns MZ4_TROE(8.5e-29, 6.5e0, 1.1e-11, 1.0, .6e0) 153 | """ 154 | return MZ4_TROE(8.5e-29, 6.5e0, 1.1e-11, 1.0, .6e0) 155 | 156 | 157 | def MZ4_USR12(): 158 | """ 159 | USR12 reaction rate as defined in MOZART4 160 | 161 | Return MZ4_USR11() * 1.111e28 * exp( -14000.0 / TEMP ) 162 | """ 163 | return MZ4_USR11() * 1.111e28 * exp(-14000.0 / TEMP) 164 | 165 | 166 | def MZ4_USR14(): 167 | """ 168 | USR14 reaction rate as defined in MOZART4 169 | 170 | Return 1.1e-11 * 300.e0/ TEMP / M 171 | """ 172 | return 1.1e-11 * 300.e0 / TEMP / M 173 | 174 | 175 | def MZ4_USR15(): 176 | """ 177 | USR15 reaction rate as defined in MOZART4 178 | 179 | Returns MZ4_USR14() * 1.111e28 * exp( -14000.e0 / TEMP ) 180 | """ 181 | return MZ4_USR14() * 1.111e28 * exp(-14000.e0 / TEMP) 182 | 183 | 184 | def MZ4_USR21(): 185 | """ 186 | USR21 reaction rate as defined in MOZART4 187 | 188 | Returns TEMP**2 * 7.69e-17 * exp( 253.e0/TEMP ) 189 | """ 190 | return TEMP**2 * 7.69e-17 * exp(253.e0/TEMP) 191 | 192 | 193 | def MZ4_USR22(): 194 | """ 195 | USR22 reaction rate as defined in MOZART4 196 | 197 | Returns 3.82e-11 * exp( -2000.0/TEMP ) + 1.33e-13 198 | """ 199 | return 3.82e-11 * exp(-2000.0/TEMP) + 1.33e-13 200 | 201 | 202 | def MZ4_USR23(): 203 | """ 204 | USR23 reaction rate as defined in MOZART4 205 | 206 | fc = 3.0e-31 *(300.0/TEMP)**3.3e0 207 | ko = fc * M / (1.0 + fc * M / 1.5e-12) 208 | Returns ko * .6e0**(1. + (log10(fc * M / 1.5e-12))**2.0)**(-1.0) 209 | """ 210 | # REAL(kind=dp) ko, fc 211 | 212 | fc = 3.0e-31 * (300.0/TEMP)**3.3e0 213 | ko = fc * M / (1.0 + fc * M / 1.5e-12) 214 | return ko * .6e0**(1. + (log10(fc * M / 1.5e-12))**2.0)**(-1.0) 215 | 216 | 217 | def MZ4_USR24(): 218 | """ 219 | USR24 reaction rate as defined in MOZART4 220 | 221 | #REAL(kind=dp) ko 222 | ko = 1.0 + 5.5e-31 * exp( 7460.0/TEMP ) * M * 0.21e0 223 | return 1.7e-42 * exp( 7810.0/TEMP ) * M * 0.21e0 / ko 224 | """ 225 | 226 | # REAL(kind=dp) ko 227 | ko = 1.0 + 5.5e-31 * exp(7460.0/TEMP) * M * 0.21e0 228 | return 1.7e-42 * exp(7810.0/TEMP) * M * 0.21e0 / ko 229 | -------------------------------------------------------------------------------- /pykpp/funcs/racm.py: -------------------------------------------------------------------------------- 1 | __all__ = ['RACM_TROE', 'RACM_TROE_EQUIL', 'RACM_THERMAL', 'RACM_THERMAL_T2'] 2 | from numpy import exp, log10 3 | 4 | TEMP = M = 0 5 | 6 | 7 | def update_func_world(mech, world): 8 | """ 9 | Function to update globals for user defined functions 10 | """ 11 | globals().update(world) 12 | 13 | 14 | def RACM_TROE(A0, B0, A1, B1): 15 | """ 16 | RACM_TROE equation as defined in the RACM SBOX model 17 | 18 | K0 = (A0 * (TEMP/300.0)**(-B0)) 19 | K1 = (A1 * (TEMP/300.0)**(-B1)) 20 | K0 = K0 * M 21 | K1 = K0 / K1 22 | 23 | Returns (K0 / (1.0 + K1))* \ 24 | CF**(1.0 / (1.0 / N + (log10(K1))**2)) 25 | """ 26 | # REAL A0, B0, A1, B1, C1 27 | # REAL(kind=dp) K0, K1 28 | # REAL(kind=dp), PARAMETER :: CF = 0.6_dp, N = 1._dp 29 | CF = 0.6 30 | N = 1. 31 | K0 = (A0 * (TEMP/300.0)**(-B0)) 32 | K1 = (A1 * (TEMP/300.0)**(-B1)) 33 | K0 = K0 * M 34 | K1 = K0 / K1 35 | return (K0 / (1.0 + K1)) * \ 36 | CF**(1.0 / (1.0 / N + (log10(K1))**2)) 37 | 38 | 39 | def RACM_TROE_EQUIL(A0, B0, A1, B1, A2, C2): 40 | """ 41 | RACM Troe equilibrium equation as defined in the RACM SBOX model 42 | 43 | #REAL A0, B0, A1, B1, A2, C2 44 | return RACM_TROE( A0, B0, A1, B1) * (1./A2 * exp(-C2 / TEMP)) 45 | """ 46 | # REAL A0, B0, A1, B1, A2, C2 47 | return RACM_TROE(A0, B0, A1, B1) * (1./A2 * exp(-C2 / TEMP)) 48 | 49 | 50 | def RACM_THERMAL(A0, B0): 51 | """ 52 | RACM Thermal equation as defined in the RACM SBOX model 53 | 54 | #REAL A0, B0 55 | # RACM2 reaction rates have the form K = A * EXP(-B / T) 56 | # 57 | # Translation adds a 0 C 58 | return (A0 * exp(-B0 / TEMP)) 59 | """ 60 | # REAL A0, B0 61 | # RACM2 reaction rates have the form K = A * EXP(-B / T) 62 | # 63 | # Translation adds a 0 C 64 | return (A0 * exp(-B0 / TEMP)) 65 | 66 | 67 | def RACM_THERMAL_T2(A0, B0): 68 | """ 69 | RACM Thermal T2 equation as defined in the RACM SBOX model 70 | 71 | # REAL A0, B0 72 | # REAL, PARAMETER :: C0 = 0. 73 | # 74 | # Translation adds a 0 C 75 | return (A0)*TEMP**2*exp(-(B0)/TEMP) 76 | """ 77 | # REAL A0, B0 78 | # REAL, PARAMETER :: C0 = 0. 79 | # 80 | # Translation adds a 0 C 81 | return (A0)*TEMP**2*exp(-(B0)/TEMP) 82 | -------------------------------------------------------------------------------- /pykpp/main.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import os 3 | from glob import glob 4 | from .mech import Mech 5 | from argparse import ArgumentParser, RawDescriptionHelpFormatter 6 | 7 | 8 | modelpaths = [ 9 | os.path.basename(p) 10 | for p in glob(os.path.join(os.path.dirname(__file__), 'models', '*.kpp')) 11 | ] 12 | 13 | parser = ArgumentParser( 14 | description='pykpp Argument Parsing', 15 | usage="""Usage: python -m pykpp mech.def 16 | 17 | Where mech.pykpp is a single file that contains initial 18 | values and reaction definitions according to the KPP 19 | format. 20 | """, 21 | formatter_class=RawDescriptionHelpFormatter 22 | ) 23 | 24 | paa = parser.add_argument 25 | paa('paths', nargs='+', help='path to a pykpp definition file') 26 | 27 | paa("-v", "--verbose", dest="verbose", action="store_true", default=False, 28 | help="Show extended output") 29 | 30 | paa("-a", "--atol", dest="atol", type=float, default=1e-3, 31 | help="absolute tolerance") 32 | 33 | paa("-r", "--rtol", dest="rtol", type=float, default=1e-4, 34 | help="relative tolerance") 35 | 36 | paa("--tstart", dest="tstart", type=float, default=None, help="Start time") 37 | 38 | paa("--tend", dest="tend", type=float, default=None, help="End time") 39 | 40 | paa("--norun", dest="norun", action='store_true', default=False, 41 | help="Don't run") 42 | 43 | paa("--dt", dest="dt", type=float, default=None, help="Time step") 44 | 45 | paa("--no-jacobian", dest="jacobian", action="store_false", default=True, 46 | help="disable use of jacobian") 47 | 48 | paa("--no-autofuncs", dest="add_default_funcs", action="store_false", 49 | default=True, help="disable use of automatically identified functions.") 50 | 51 | paa("-k", "--keywords", dest="keywords", type=str, default='hv,PROD,EMISSION', 52 | help=("List of keywords to be ignored in reactants or products (comma " 53 | + " delimited; default='hv')")) 54 | 55 | paa("-o", "--outpath", dest="outpath", type=str, default=None, 56 | help="Output path.") 57 | 58 | paa("-m", "--monitor", dest="monitor", type=str, default=None, 59 | help="Extra monitor values (comma separated string).") 60 | 61 | paa("-s", "--solver", dest="solver", default=None, 62 | help=("solver (default: lsoda; vode; zvode; dopri5; dop853) with optional" 63 | + " keywords comma delimited (e.g., lsoda,atol=1e-2,rtol=1e-5," 64 | + "max_order_s=2,max_order_ns=2,nsteps=1000)")) 65 | 66 | paa("-c", "--code", dest="code", default="", 67 | help=("code to solve (exec) after model is compiled and run (unless " 68 | + "--norun); out is a keyword for the mechanism that has been " 69 | + "generated")) 70 | 71 | parser.epilog = """ 72 | 73 | functions for world updating can be explored using pydoc pykpp.updaters 74 | 75 | functions for reaction rates and world updating can be explored using pydoc 76 | pykpp.stdfuncs 77 | 78 | run existing models (pykpp modelfile.kpp):\n\t""" + '\n\t'.join(modelpaths) 79 | 80 | 81 | def main(globals={}, options=None): 82 | if options is None: 83 | options = parser.parse_args() 84 | 85 | if len(options.paths) == 0: 86 | parser.print_help() 87 | exit() 88 | 89 | outs = [] 90 | for arg in options.paths: 91 | keywords = [k_.strip() for k_ in options.keywords.split(',')] 92 | out = Mech( 93 | arg, verbose=options.verbose, keywords=keywords, 94 | add_default_funcs=options.add_default_funcs 95 | ) 96 | if options.monitor is not None: 97 | out.monitor = tuple([ 98 | (None if k not in out.allspcs else out.allspcs.index(k), k) 99 | for k in options.monitor.split(',') 100 | ]) + out.monitor 101 | 102 | if not options.norun: 103 | if options.solver is not None: 104 | solver = options.solver.split(',')[0] 105 | solver_keywords = eval( 106 | 'dict(' + ','.join(options.solver.split(',')[1:]) + ')' 107 | ) 108 | else: 109 | solver = options.solver 110 | solver_keywords = {} 111 | runtime = out.run( 112 | tstart=options.tstart, tend=options.tend, dt=options.dt, 113 | solver=solver, jac=options.jacobian, **solver_keywords 114 | ) 115 | print('Solved in %f seconds' % runtime) 116 | 117 | outs.append(out) 118 | 119 | if os.path.exists(options.code): 120 | options.code = open(options.code, 'r').read() 121 | 122 | exec(options.code) 123 | globals.update(locals()) 124 | 125 | 126 | if __name__ == '__main__': 127 | main(globals=globals()) 128 | -------------------------------------------------------------------------------- /pykpp/models/MCM_J.def: -------------------------------------------------------------------------------- 1 | translation = {1:compile('TUV_J(2,THETA)', '', 'eval'), 2 | 2:compile('TUV_J(3,THETA)', '', 'eval'), 3 | 3:compile('TUV_J(5,THETA)', '', 'eval'), 4 | 4:compile('TUV_J(6,THETA)', '', 'eval'), 5 | 5:compile('TUV_J(7,THETA)', '', 'eval'), 6 | 6:compile('TUV_J(8,THETA)', '', 'eval'), 7 | 7:compile('TUV_J(12,THETA)', '', 'eval'), 8 | 8:compile('TUV_J(13,THETA)', '', 'eval'), 9 | 11:compile('TUV_J(17,THETA)', '', 'eval'), 10 | 12:compile('TUV_J(18,THETA)', '', 'eval'), 11 | 13:compile('TUV_J(19,THETA)', '', 'eval'), 12 | 14:compile('TUV_J(22,THETA)', '', 'eval'), 13 | 15:compile('TUV_J(22,THETA)', '', 'eval'), #Mapped to 22 14 | 16:compile('TUV_J(22,THETA)', '', 'eval'), #Mapped to 22. Needs scaling factor 15 | 17:compile('TUV_J(22,THETA)', '', 'eval'), #Mapped to 22 16 | 21:compile('TUV_J(26,THETA)', '', 'eval'), 17 | 18:compile('.5*TUV_J(25,THETA)', '', 'eval'), #Assume pathway splits 50% 18 | 19:compile('.5*TUV_J(25,THETA)', '', 'eval'), 19 | 22:compile('TUV_J(28,THETA)', '', 'eval'), 20 | 23:compile('.5*TUV_J(27,THETA)', '', 'eval'), 21 | 24:compile('.5*TUV_J(27,THETA)', '', 'eval'), #Assume pathway splits 50% 22 | 31:0 #No longer a part of TUV 23 | 32:compile('TUV_J(45,THETA)', '', 'eval'), 24 | 33:compile('TUV_J(44,THETA)', '', 'eval'), 25 | 34:compile('TUV_J(46,THETA)', '', 'eval'), 26 | 35:compile('TUV_J(47,THETA)', '', 'eval'), 27 | 41:compile('TUV_J(31,THETA)', '', 'eval'), 28 | 51:compile('TUV_J(34,THETA)', '', 'eval'), 29 | 52:compile('TUV_J(35,THETA)', '', 'eval'), 30 | 53:compile('TUV_J(35,THETA)', '', 'eval'), 31 | 54:compile('TUV_J(36,THETA)', '', 'eval'), 32 | 55:compile('TUV_J(39,THETA)', '', 'eval'), 33 | 56:compile('TUV_J(38,THETA)', '', 'eval'), 34 | 57:0} #No longer part of TUV 35 | def J(idx): 36 | return eval(translation[idx], None, None) 37 | -------------------------------------------------------------------------------- /pykpp/models/UFAuxMech09.eqn: -------------------------------------------------------------------------------- 1 | // Chamber wall mechanism 2 | #EQUATIONS 3 | H2O2 = : 1.1E-3 ; 4 | O3 = : 1.91E-6 ; 5 | SO2 = : 9.72E-6 ; 6 | N2O5 = 2.0 WHNO3 : 4.2E-5; 7 | N2O5 + WHNO3 = 2.0 NO + NO2 : 9.0E-22*exp(2000.0/TEMP) ; 8 | HNO3 = WHNO3 : 8.2E-5 ; 9 | HNO3 + WH2O = WHNO3 : 2.5E-21 ; 10 | WHNO3 = NO2 : 8.0e-4 * TUV_J5pt0('NO2 -> NO + O(3P)', THETA); 11 | NO + WH2O = WHNO3 : 6.77E-24 ; 12 | NO2 + WH2O = WHNO3 : 6.77E-24 ; 13 | NO + NO2 + WHNO3 = 2.0 HONO + 1.0 NO2 : 5.0E-30; 14 | NO + WHNO3 = 1.5 HONO + 0.5 NO2 : 6.09E-17 ; 15 | NO2 + WHNO3 = 1.5 HONO + 0.5 NO2 : 3.38E-18 ; 16 | NO2 = HONO : 1e-3 * TUV_J5pt0('NO2 -> NO + O(3P)', THETA); 17 | HONO + WH2O = WHONO : 4.5E-21 ; 18 | WHONO = HONO : 3.3E-3 ; 19 | -------------------------------------------------------------------------------- /pykpp/models/atoms: -------------------------------------------------------------------------------- 1 | #ATOMS 2 | H { 1 Hydrogen }; 3 | He { 2 Helium }; 4 | Li { 3 Litium }; 5 | Be { 4 }; 6 | B { 5 }; 7 | C { 6 }; 8 | N { 7 }; 9 | O { 8 }; 10 | F { 9 }; 11 | Ne { 10 }; 12 | Na { 11 }; 13 | Mg { 12 }; 14 | Al { 13 }; 15 | Si { 14 }; 16 | P { 15 }; 17 | S { 16 }; 18 | Cl { 17 }; 19 | Ar { 18 }; 20 | K { 19 }; 21 | Ca { 20 }; 22 | Sc { 21 }; 23 | Ti { 22 }; 24 | V { 23 }; 25 | Cr { 24 }; 26 | Mn { 25 }; 27 | Fe { 26 }; 28 | Co { 27 }; 29 | Ni { 28 }; 30 | Cu { 29 }; 31 | Zn { 30 }; 32 | Ga { 31 }; 33 | Ge { 32 }; 34 | As { 33 }; 35 | Se { 34 }; 36 | Br { 35 }; 37 | Kr { 36 }; 38 | Rb { 37 }; 39 | Sr { 38 }; 40 | Y { 39 }; 41 | Zr { 40 }; 42 | Nb { 41 }; 43 | Mu { 42 }; 44 | Tc { 43 }; 45 | Ru { 44 }; 46 | Rh { 45 }; 47 | Pd { 46 }; 48 | Ag { 47 }; 49 | Cd { 48 }; 50 | In { 49 }; 51 | Sn { 50 }; 52 | Sb { 51 }; 53 | Te { 52 }; 54 | I { 53 }; 55 | Xe { 54 }; 56 | Cs { 55 }; 57 | Ba { 56 }; 58 | La { 57 }; 59 | Ce { 58 }; 60 | Pr { 59 }; 61 | Nd { 60 }; 62 | Pm { 61 }; 63 | Sm { 62 }; 64 | Eu { 63 }; 65 | Gd { 64 }; 66 | Tb { 65 }; 67 | Dy { 66 }; 68 | Ho { 67 }; 69 | Er { 68 }; 70 | Tm { 69 }; 71 | Yb { 70 }; 72 | Lu { 71 }; 73 | Hf { 72 }; 74 | Ta { 73 }; 75 | W { 74 }; 76 | Re { 75 }; 77 | Os { 76 }; 78 | Ir { 77 }; 79 | Pt { 78 }; 80 | Au { 79 }; 81 | Hg { 80 }; 82 | Tl { 81 }; 83 | Pb { 82 }; 84 | Bi { 83 }; 85 | Po { 84 }; 86 | At { 85 }; 87 | Rn { 86 }; 88 | Fr { 87 }; 89 | Ra { 88 }; 90 | Ac { 89 }; 91 | Th { 90 }; 92 | Pa { 91 }; 93 | U { 92 }; 94 | Np { 93 }; 95 | Pu { 94 }; 96 | Am { 95 }; 97 | Cm { 96 }; 98 | Bk { 97 }; 99 | Cf { 98 }; 100 | Es { 99 }; 101 | Fm {100 }; 102 | Md {101 }; 103 | No {102 }; 104 | Lr {103 }; 105 | Unq {104 }; 106 | Unp {105 }; 107 | Unh {106 }; 108 | -------------------------------------------------------------------------------- /pykpp/models/cbm4.eqn: -------------------------------------------------------------------------------- 1 | #EQUATIONS 2 | < 1.> NO2 = NO + O : 8.89E-3*SUN ; 3 | < 2.> O = O3 : ARR2(1.4E+3, 1175.0) ; 4 | < 3.> O3 + NO = NO2 : ARR2(1.8E-12, -1370.0) ; 5 | < 4.> O + NO2 = NO : 9.3E-12 ; 6 | < 5.> O + NO2 = NO3 : ARR2(1.6E-13, 687.0) ; 7 | < 6.> O + NO = NO2 : ARR2(2.2E-13, 602.0) ; 8 | < 7.> O3 + NO2 = NO3 : ARR2(1.2E-13, -2450.0) ; 9 | < 8.> O3 = O : 3.556E-04*SUN ; 10 | < 9.> O3 = O1D : 2.489E-05*SUN ; 11 | <10.> O1D = O : ARR2(1.9E+8, 390.0) ; 12 | <11.> O1D + H2O = 2OH : 2.2E-10 ; 13 | <12.> O3 + OH = HO2 : ARR2(1.6E-12, -940.0) ; 14 | <13.> O3 + HO2 = OH : ARR2(1.4E-14, -580.0) ; 15 | <14.> NO3 = 0.89 NO2 + 0.89 O + 0.11 NO : 1.378E-01*SUN ; 16 | <15.> NO3 + NO = 2 NO2 : ARR2(1.3E-11, 250.0) ; 17 | <16.> NO3 + NO2 = NO + NO2 : ARR2(2.5E-14, -1230.0) ; 18 | <17.> NO3 + NO2 = N2O5 : ARR2(5.3E-13, 256.0) ; 19 | <18.> N2O5 + H2O = 2 HNO3 : 1.3E-21 ; 20 | <19.> N2O5 = NO3 + NO2 : ARR2(3.5E+14, -10897.0) ; 21 | <20.> 2 NO = 2 NO2 : ARR2(1.8E-20, 530.0) ; 22 | <21.> NO + NO2 + H2O = 2 HONO : 4.4E-40 ; 23 | <22.> OH + NO = HONO : ARR2(4.5E-13, 806.0) ; 24 | <23.> HONO = OH + NO : 1.511e-03*SUN ; 25 | <24.> OH + HONO = NO2 : 6.6E-12 ; 26 | <25.> 2 HONO = NO + NO2 : 1.0E-20 ; 27 | <26.> OH + NO2 = HNO3 : ARR2(1.0E-12, 713.0) ; 28 | <27.> OH + HNO3 = NO3 : ARR2(5.1E-15, 1000.0) ; 29 | <28.> HO2 + NO = OH + NO2 : ARR2(3.7E-12, 240.0) ; 30 | <29.> HO2 + NO2 = PNA : ARR2(1.2E-13, 749.0) ; 31 | <30.> PNA = HO2 + NO2 : ARR2(4.8E+13, -10121.0) ; 32 | <31.> OH + PNA = NO2 : ARR2(1.3E-12, 380.0) ; 33 | <32.> 2 HO2 = H2O2 : ARR2(5.9E-14, 1150.0) ; 34 | <33.> 2 HO2 + H2O = H2O2 : ARR2(2.2E-38, 5800.0) ; 35 | <34.> H2O2 = 2 OH : 6.312E-06*SUN ; 36 | <35.> OH + H2O2 = HO2 : ARR2(3.1E-12, -187.0) ; 37 | <36.> OH + CO = HO2 : 2.2E-13 ; 38 | <37.> HCHO + OH = HO2 + CO : 1.0E-11 ; 39 | <38.> HCHO = 2 HO2 + CO : 2.845E-05*SUN ; 40 | <39.> HCHO = CO : 3.734E-05*SUN ; 41 | <40.> HCHO + O = OH + HO2 + CO : ARR2(3.0E-11, -1550.0) ; 42 | <41.> HCHO + NO3 = HNO3 + HO2 + CO : 6.3E-16 ; 43 | <42.> ALD2 + O = C2O3 + OH : ARR2(1.2E-11, -986.0) ; 44 | <43.> ALD2 + OH = C2O3 : ARR2(7.0E-12, 250.0) ; 45 | <44.> ALD2 + NO3 = C2O3 + HNO3 : 2.5E-15 ; 46 | <45.> ALD2 = HCHO + XO2 + CO + 2 HO2 : 4.00E-06*SUN ; 47 | <46.> C2O3 + NO = HCHO + XO2 + HO2 + NO2 : ARR2(5.4E-12, 250.0) ; 48 | <47.> C2O3 + NO2 = PAN : ARR2(8.0E-20, 5500.0) ; 49 | <48.> PAN = C2O3 + NO2 : ARR2(9.4E+16, -14000.0) ; 50 | <49.> 2 C2O3 = 2 HCHO + 2 XO2 + 2 HO2 : 2.0E-12 ; 51 | <50.> C2O3 + HO2 = 0.79 HCHO + 0.79 XO2 + 0.79 HO2 + 0.79 OH : 6.5E-12 ; 52 | <51.> OH = HCHO + XO2 + HO2 : ARR2(1.1E+2, -1710.0) ; 53 | <52.> PAR + OH = 0.87 XO2 + 0.13 XO2N + 0.11 HO2 + 0.11 ALD2 + 0.76 ROR + -0.11 PAR : 8.1E-13 ; 54 | <53.> ROR = 1.1 ALD2 + 0.96 XO2 + 0.94 HO2 + 0.04 XO2N + 0.02 ROR + -2.10 PAR : ARR2(1.0E+15, -8000.0) ; 55 | <54.> ROR = HO2 : 1.6E+03 ; 56 | <55.> ROR + NO2 = PROD : 1.5E-11 ; 57 | <56.> O + OLE = 0.63 ALD2 + 0.38 HO2 + 0.28 XO2 + 0.3 CO + 0.2 HCHO + 0.02 XO2N + 0.22 PAR + 0.2 OH : ARR2(1.2E-11, -324.0) ; 58 | <57.> OH + OLE = HCHO + ALD2 + XO2 + HO2 + -1 PAR : ARR2(5.2E-12, 504.0) ; 59 | <58.> O3 + OLE = 0.5 ALD2 + 0.74 HCHO + 0.33 CO + 0.44 HO2 + 0.22 XO2 + 0.1 OH + -1 PAR : ARR2(1.4E-14, -2105.0) ; 60 | <59.> NO3 + OLE = 0.91 XO2 + HCHO + ALD2 + 0.09 XO2N + NO2 + -1 PAR : 7.7E-15 ; 61 | <60.> O + ETH = HCHO + 0.7 XO2 + CO + 1.7 HO2 + 0.3 OH : ARR2(1.0E-11, -792.0) ; 62 | <61.> OH + ETH = XO2 + 1.56 HCHO + HO2 + 0.22 ALD2 : ARR2(2.0E-12, 411.0) ; 63 | <62.> O3 + ETH = HCHO + 0.42 CO + 0.12 HO2 : ARR2(1.3E-14, -2633.0) ; 64 | <63.> OH + TOL = 0.08 XO2 + 0.36 CRES + 0.44 HO2 + 0.56 TO2 : ARR2(2.1E-12, 322.0) ; 65 | <64.> TO2 + NO = 0.9 NO2 + 0.9 OPEN + 0.9 HO2 : 8.1E-12 ; 66 | <65.> TO2 = HO2 + CRES : 4.20 ; 67 | <66.> OH + CRES = 0.4 CRO + 0.6 XO2 + 0.6 HO2 + 0.3 OPEN : 4.1E-11 ; 68 | <67.> NO3 + CRES = CRO + HNO3 : 2.2E-11 ; 69 | <68.> CRO + NO2 = PROD : 1.4E-11 ; 70 | <69.> OH + XYL = 0.7 HO2 + 0.5 XO2 + 0.2 CRES + 0.8 MGLY + 1.10 PAR + 0.3 TO2 : ARR2(1.7E-11, 116.0) ; 71 | <70.> OH + OPEN = XO2 + C2O3 + 2 HO2 + 2 CO + HCHO : 3.0E-11 ; 72 | <71.> OPEN = C2O3 + CO + HO2 : 5.334E-05*SUN ; 73 | <72.> O3 + OPEN = 0.03 ALD2 + 0.62 C2O3 + 0.7 HCHO + 0.03 XO2 + 0.69 CO + 0.08 OH + 0.76 HO2 + 0.2 MGLY : ARR2(5.4E-17, -500.0) ; 74 | <73.> OH + MGLY = XO2 + C2O3 : 1.70E-11 ; 75 | <74.> MGLY = C2O3 + CO + HO2 : 1.654E-04*SUN ; 76 | <75.> O + ISOP = 0.6 HO2 + 0.8 ALD2 + 0.55 OLE + 0.5 XO2 + 0.5 CO + 0.45 ETH + 0.9 PAR : 1.80E-11 ; 77 | <76.> OH + ISOP = HCHO + XO2 + 0.67 HO2 + 0.4 MGLY + 0.2 C2O3 + ETH + 0.2 ALD2 + 0.13 XO2N : 9.6E-11 ; 78 | <77.> O3 + ISOP = HCHO + 0.4 ALD2 + 0.55 ETH + 0.2 MGLY + 0.06 CO + 0.1 PAR + 0.44 HO2 + 0.1 OH : 1.2E-17 ; 79 | <78.> NO3 + ISOP = XO2N : 3.2E-13 ; 80 | <79.> XO2 + NO = NO2 : 8.1E-12 ; 81 | <80.> 2 XO2 = PROD : ARR2(1.7E-14, 1300.0) ; 82 | <81.> XO2N + NO = PROD : 6.8E-13 ; 83 | -------------------------------------------------------------------------------- /pykpp/models/cbm4.kpp: -------------------------------------------------------------------------------- 1 | #INITVALUES 2 | P = 101325. 3 | TEMP = 288.15 4 | CFACTOR = P / (R / centi**3) / TEMP * Avogadro * 1e-9; 5 | NO = 50.0; 6 | NO2 = 20.0; 7 | HONO = 1.0; 8 | O3 = 100.0; 9 | HCHO = 10.0; 10 | ALD2 = 10; 11 | PAN = 1.0; 12 | PAR = 50.0; 13 | OLE = 10.0; 14 | ETH = 10.0; 15 | TOL = 10.0; 16 | XYL = 10.0; 17 | ISOP = 10.0; 18 | CO = 300.0; 19 | H2O = 1.25E+8; 20 | 21 | #include cbm4.eqn 22 | 23 | #INLINE PY_UTIL 24 | add_world_updater(func_updater(Update_M, incr = 300)) 25 | add_world_updater(func_updater(Update_SUN, incr = 300)) 26 | #ENDINLINE 27 | 28 | #INLINE PY_INIT 29 | TSTART = 12 * 3600. 30 | TEND = TSTART + 5 * 3600. * 24 31 | DT = 600. 32 | StartDate = 'datetime(2013, 6, 1)' 33 | Latitude_Degrees = 35. 34 | Longitude_Degrees = 0.00E+00 35 | #ENDINLINE 36 | #MONITOR O3; NO; -------------------------------------------------------------------------------- /pykpp/models/chimere1.def: -------------------------------------------------------------------------------- 1 | 2 | #INITVALUES 3 | TEMP = 298. 4 | P = 101325. 5 | CFACTOR = P * Avogadro / R / TEMP * centi **3 * micro {ppm-to-molecules/cm3} 6 | ALL_SPEC=1e-5; 7 | M = 1e6 8 | TOL = 195.81; 9 | OXYL = 195.81; 10 | NO = 77.75; 11 | NO2 = 25.06; 12 | SO2 = 48.36; 13 | O3 = 0.1; 14 | NH3 = 0.1; 15 | H2O = 6.11 * 10**((7.5 * 11.85)/(11.85+237.3)) * 50. / 100 * 100 / P * 1e6 16 | 17 | #INLINE PY_UTIL 18 | add_world_updater(func_updater(Update_M, incr = 300)) 19 | add_world_updater(func_updater(Update_THETA, incr = 300)) 20 | #ENDINLINE 21 | #INLINE PY_INIT 22 | TSTART = 5.5 * 3600. 23 | TEND = TSTART + 3600. * 8. 24 | DT = 120. 25 | StartDate = 'datetime(2012, 5, 18)' 26 | Latitude_Degrees = 2.50E+01 27 | Longitude_Degrees = 0.00E+00 28 | #ENDINLINE 29 | 30 | #MONITOR O3; NO2; NO; HNO3; 31 | 32 | #include melchior1.eqn 33 | #include melchior1_soa_cpx.eqn 34 | -------------------------------------------------------------------------------- /pykpp/models/geoschem_v11.def: -------------------------------------------------------------------------------- 1 | #INITVALUES 2 | P = 3.01E+02 * 100 3 | TEMP = 2.35E+02 4 | ALL_SPEC = 1e-20 5 | CFACTOR = P * Avogadro / R / TEMP * centi **3 * micro {ppm-to-molecules/cm3} 6 | ACET = 1.49E-03 7 | ALD2 = 1.17E-04 8 | ALK4 = 7.70E-05 9 | C2H6 = 8.30E-04 10 | C3H8 = 2.81E-04 11 | CH2O = 4.03E-04 12 | CH4 = 1.78E+00 13 | CO = 1.07E-01 14 | EOH = 2.08E-04 15 | H2O = 2.87E+02 16 | H2O2 = 2.08E-04 17 | HNO3 = 1.19E-04 18 | HNO4 = 6.78E-05 19 | HO2 = 1.16E-05 20 | MAP = 2.25E-04 21 | MEK = 9.50E-05 22 | MNO3 = 2.37E-06 23 | MOH = 2.90E-03 24 | MP = 2.33E-04 25 | MPN = 3.25E-05 26 | N2O = 3.30E-01 27 | NO = 3.38E-04 28 | NO2 = 1.33E-04 29 | O3 = 7.05E-02 30 | OCS = 4.49E-04 31 | OH = 5.62E-07 32 | PAN = 3.73E-04 33 | PPN = 3.73E-05 34 | PRPE = 1.50E-06 35 | R4N2 = 7.86E-06 36 | SO2 = 2.06E-05 37 | 38 | 39 | #INLINE PY_INIT 40 | add_world_updater(func_updater(Update_M, incr = 300.)) 41 | add_world_updater(func_updater(Update_THETA, incr = 300.)) 42 | TSTART = 43200 43 | TEND = 43200 + 10 * 24 * 3600. 44 | DT = 300. 45 | MONITOR_DT = 3600. 46 | StartDate = 'datetime(2000, 7, 1)' 47 | Latitude_Degrees = 4.50E+01 48 | Longitude_Degrees = 0.00E+00 49 | atol = ones(NSPCS, dtype = 'd')*1e-3 50 | atol[ind_O1D] *= 1e5 51 | atol[ind_ClO] *= 1e5 52 | atol[ind_Cl] *= 1e5 53 | #ENDINLINE 54 | 55 | #include geoschem_standard_v11.eqn 56 | 57 | #MONITOR O3; NO2; H2O; N2; O2; ClO; -------------------------------------------------------------------------------- /pykpp/models/geossuper_chem.kpp: -------------------------------------------------------------------------------- 1 | #INITVALUES 2 | P = 3.01E+02 * 100 3 | TEMP = 2.35E+02 4 | CFACTOR = P * Avogadro / R / TEMP * centi **3 * micro {ppm-to-molecules/cm3} 5 | ACET = 1.49E-03 6 | ALD2 = 1.17E-04 7 | ALK4 = 7.70E-05 8 | C2H6 = 8.30E-04 9 | C3H8 = 2.81E-04 10 | CH2O = 4.03E-04 11 | CH4 = 1.78E+00 12 | CO = 1.07E-01 13 | EOH = 2.08E-04 14 | H2O = 2.87E+02 15 | H2O2 = 2.08E-04 16 | HNO3 = 1.19E-04 17 | HNO4 = 6.78E-05 18 | HO2 = 1.16E-05 19 | MAP = 2.25E-04 20 | MEK = 9.50E-05 21 | MNO3 = 2.37E-06 22 | MOH = 2.90E-03 23 | MP = 2.33E-04 24 | MPN = 3.25E-05 25 | N2O = 3.30E-01 26 | NO = 3.38E-04 27 | NO2 = 1.33E-04 28 | O3 = 7.05E-02 29 | OCS = 4.49E-04 30 | OH = 5.62E-07 31 | PAN = 3.73E-04 32 | PPN = 3.73E-05 33 | PRPE = 1.50E-06 34 | R4N2 = 7.86E-06 35 | SO2 = 2.06E-05 36 | 37 | 38 | #INLINE PY_INIT 39 | add_world_updater(func_updater(Update_M, incr = 300.)) 40 | add_world_updater(func_updater(Update_THETA, incr = 300.)) 41 | TSTART = 43200 42 | TEND = 43200 + 10 * 24 * 3600. 43 | DT = 3600. 44 | StartDate = 'datetime(2000, 7, 1)' 45 | Latitude_Degrees = 4.50E+01 46 | Longitude_Degrees = 0.00E+00 47 | 48 | #ENDINLINE 49 | 50 | #include geossuper_chem.eqn 51 | 52 | #MONITOR O3; NO2; H2O; N2; O2; -------------------------------------------------------------------------------- /pykpp/models/mcm_ch4.kpp: -------------------------------------------------------------------------------- 1 | #INLINE PY_INIT 2 | DT = 300 3 | TSTART = 5 * 3600. 4 | TEND = TSTART + 8 * 3600 5 | P = 101325. 6 | TEMP = 298. 7 | H2O = h2o_from_rh_and_temp(80., TEMP) 8 | StartDate = 'datetime(2013, 6, 1)' 9 | Latitude_Degrees = 35. 10 | Longitude_Degrees = 0.00E+00 11 | #ENDINLINE 12 | 13 | #INITVALUES 14 | CFACTOR = P * Avogadro / R / TEMP * centi **3 * nano {ppb-to-molecules/cm3} 15 | ALL_SPEC=1e-32; 16 | CH4 = 1850. 17 | NO = 1. 18 | NO2 = 7. 19 | O3 = 60. 20 | 21 | #MONITOR O3; NO2; RO2/CFACTOR; 22 | #INLINE PY_UTIL 23 | add_world_updater(func_updater(Update_M, incr = 300)) 24 | add_world_updater(func_updater(Update_THETA, incr = 300)) 25 | add_world_updater(code_updater(""" 26 | EXP = exp 27 | LOG10 = log10 28 | try: 29 | RO2 = y[[ind_CH3O2]].sum() 30 | except: 31 | warn('Using RO2 = 1e-32 @ %s' % t) 32 | RO2 = 1e-32 33 | 34 | KRO2NO = 2.7e-12*EXP(360/TEMP) 35 | KRO2HO2 = 2.91e-13*EXP(1300/TEMP) 36 | KAPHO2 = 5.2e-13*EXP(980/TEMP) 37 | KAPNO = 7.5e-12*EXP(290/TEMP) 38 | KRO2NO3 = 2.3e-12 39 | KNO3AL = 1.4e-12*EXP(-1860/TEMP) 40 | KDEC = 1.00e+06 41 | KROPRIM = 2.50e-14*EXP(-300/TEMP) 42 | KROSEC = 2.50e-14*EXP(-300/TEMP) 43 | KCH3O2 = 1.03e-13*EXP(365/TEMP) 44 | K298CH3O2 = 3.5e-13 45 | K14ISOM1 = 3.00e7*EXP(-5300/TEMP) 46 | KD0 = 1.10e-05*M*EXP(-10100/TEMP) 47 | KDI = 1.90e17*EXP(-14100/TEMP) 48 | KRD = KD0/KDI 49 | FCD = 0.30 50 | NCD = 0.75-1.27*(LOG10(FCD)) 51 | FD = 10**(LOG10(FCD)/(1+(LOG10(KRD)/NCD)**2)) 52 | KBPAN = (KD0*KDI)*FD/(KD0+KDI) 53 | KC0 = 3.28e-28*M*(TEMP/300)**(-6.87) 54 | KCI = 1.125e-11*(TEMP/300)**(-1.105) 55 | KRC = KC0/KCI 56 | FCC = 0.30 57 | NC = 0.75-1.27*(LOG10(FCC)) 58 | FC = 10**(LOG10(FCC)/(1+(LOG10(KRC)/NC)**2)) 59 | KFPAN = (KC0*KCI)*FC/(KC0+KCI) 60 | K10 = 1.0e-31*M*(TEMP/300)**(-1.6) 61 | K1I = 5.0e-11*(TEMP/300)**(-0.3) 62 | KR1 = K10/K1I 63 | FC1 = 0.85 64 | NC1 = 0.75-1.27*(LOG10(FC1)) 65 | F1 = 10**(LOG10(FC1)/(1+(LOG10(KR1)/NC1)**2)) 66 | KMT01 = (K10*K1I)*F1/(K10+K1I) 67 | K20 = 1.3e-31*M*(TEMP/300)**(-1.5) 68 | K2I = 2.3e-11*(TEMP/300)**(0.24) 69 | KR2 = K20/K2I 70 | FC2 = 0.6 71 | NC2 = 0.75-1.27*(LOG10(FC2)) 72 | F2 = 10**(LOG10(FC2)/(1+(LOG10(KR2)/NC2)**2)) 73 | KMT02 = (K20*K2I)*F2/(K20+K2I) 74 | K30 = 3.6e-30*M*(TEMP/300)**(-4.1) 75 | K3I = 1.9e-12*(TEMP/300)**(0.2) 76 | KR3 = K30/K3I 77 | FC3 = 0.35 78 | NC3 = 0.75-1.27*(LOG10(FC3)) 79 | F3 = 10**(LOG10(FC3)/(1+(LOG10(KR3)/NC3)**2)) 80 | KMT03 = (K30*K3I)*F3/(K30+K3I) 81 | K40 = 1.3e-3*M*(TEMP/300)**(-3.5)*EXP(-11000/TEMP) 82 | K4I = 9.7e+14*(TEMP/300)**(0.1)*EXP(-11080/TEMP) 83 | KR4 = K40/K4I 84 | FC4 = 0.35 85 | NC4 = 0.75-1.27*(LOG10(FC4)) 86 | F4 = 10**(LOG10(FC4)/(1+(LOG10(KR4)/NC4)**2)) 87 | KMT04 = (K40*K4I)*F4/(K40+K4I) 88 | KMT05 = 1.44e-13*(1+(M/4.2e+19)) 89 | KMT06 = 1 + (1.40e-21*EXP(2200/TEMP)*H2O) 90 | K70 = 7.4e-31*M*(TEMP/300)**(-2.4) 91 | K7I = 3.3e-11*(TEMP/300)**(-0.3) 92 | KR7 = K70/K7I 93 | FC7 = 0.81 94 | NC7 = 0.75-1.27*(LOG10(FC7)) 95 | F7 = 10**(LOG10(FC7)/(1+(LOG10(KR7)/NC7)**2)) 96 | KMT07 = (K70*K7I)*F7/(K70+K7I) 97 | K80 = 3.2e-30*M*(TEMP/300)**(-4.5) 98 | K8I = 3.0e-11 99 | KR8 = K80/K8I 100 | FC8 = 0.41 101 | NC8 = 0.75-1.27*(LOG10(FC8)) 102 | F8 = 10**(LOG10(FC8)/(1+(LOG10(KR8)/NC8)**2)) 103 | KMT08 = (K80*K8I)*F8/(K80+K8I) 104 | K90 = 1.4e-31*M*(TEMP/300)**(-3.1) 105 | K9I = 4.0e-12 106 | KR9 = K90/K9I 107 | FC9 = 0.4 108 | NC9 = 0.75-1.27*(LOG10(FC9)) 109 | F9 = 10**(LOG10(FC9)/(1+(LOG10(KR9)/NC9)**2)) 110 | KMT09 = (K90*K9I)*F9/(K90+K9I) 111 | K100 = 4.10e-05*M*EXP(-10650/TEMP) 112 | K10I = 6.0e+15*EXP(-11170/TEMP) 113 | KR10 = K100/K10I 114 | FC10 = 0.4 115 | NC10 = 0.75-1.27*(LOG10(FC10)) 116 | F10 = 10**(LOG10(FC10)/(1+(LOG10(KR10)/NC10)**2)) 117 | KMT10 = (K100*K10I)*F10/(K100+K10I) 118 | K1 = 2.40e-14*EXP(460/TEMP) 119 | K3 = 6.50e-34*EXP(1335/TEMP) 120 | K4 = 2.70e-17*EXP(2199/TEMP) 121 | K2 = (K3*M)/(1+(K3*M/K4)) 122 | KMT11 = K1 + K2 123 | K120 = 2.5e-31*M*(TEMP/300)**(-2.6) 124 | K12I = 2.0e-12 125 | KR12 = K120/K12I 126 | FC12 = 0.53 127 | NC12 = 0.75-1.27*(LOG10(FC12)) 128 | F12 = 10**(LOG10(FC12)/(1.0+(LOG10(KR12)/NC12)**2)) 129 | KMT12 = (K120*K12I*F12)/(K120+K12I) 130 | K130 = 2.5e-30*M*(TEMP/300)**(-5.5) 131 | K13I = 1.8e-11 132 | KR13 = K130/K13I 133 | FC13 = 0.36 134 | NC13 = 0.75-1.27*(LOG10(FC13)) 135 | F13 = 10**(LOG10(FC13)/(1+(LOG10(KR13)/NC13)**2)) 136 | KMT13 = (K130*K13I)*F13/(K130+K13I) 137 | K140 = 9.0e-5*EXP(-9690/TEMP)*M 138 | K14I = 1.1e+16*EXP(-10560/TEMP) 139 | KR14 = K140/K14I 140 | FC14 = 0.36 141 | NC14 = 0.75-1.27*(LOG10(FC14)) 142 | F14 = 10**(LOG10(FC14)/(1+(LOG10(KR14)/NC14)**2)) 143 | KMT14 = (K140*K14I)*F14/(K140+K14I) 144 | K150 = 8.6e-29*M*(TEMP/300)**(-3.1) 145 | K15I = 9.0e-12*(TEMP/300)**(-0.85) 146 | KR15 = K150/K15I 147 | FC15 = 0.48 148 | NC15 = 0.75-1.27*(LOG10(FC15)) 149 | F15 = 10**(LOG10(FC15)/(1+(LOG10(KR15)/NC15)**2)) 150 | KMT15 = (K150*K15I)*F15/(K150+K15I) 151 | K160 = 8e-27*M*(TEMP/300)**(-3.5) 152 | K16I = 3.0e-11*(TEMP/300)**-1 153 | KR16 = K160/K16I 154 | FC16 = 0.5 155 | NC16 = 0.75-1.27*(LOG10(FC16)) 156 | F16 = 10**(LOG10(FC16)/(1+(LOG10(KR16)/NC16)**2)) 157 | KMT16 = (K160*K16I)*F16/(K160+K16I) 158 | K170 = 5.0e-30*M*(TEMP/300)**(-1.5) 159 | K17I = 1.0e-12 160 | KR17 = K170/K17I 161 | FC17 = 0.17*EXP(-51/TEMP)+EXP(-TEMP/204) 162 | NC17 = 0.75-1.27*(LOG10(FC17)) 163 | F17 = 10**(LOG10(FC17)/(1.0+(LOG10(KR17)/NC17)**2)) 164 | KMT17 = (K170*K17I*F17)/(K170+K17I) 165 | KMT18 = 9.5e-39*O2*EXP(5270/TEMP)/(1+7.5e-29*O2*EXP(5610/TEMP)) 166 | KPPN0 = 1.7e-03*EXP(-11280/TEMP)*M 167 | KPPNI = 8.3e+16*EXP(-13940/TEMP) 168 | KRPPN = KPPN0/KPPNI 169 | FCPPN = 0.36 170 | NCPPN = 0.75-1.27*(LOG10(FCPPN)) 171 | FPPN = 10**(LOG10(FCPPN)/(1+(LOG10(KRPPN)/NCPPN)**2)) 172 | KBPPN = (KPPN0*KPPNI)*FCPPN/(KPPN0+KPPNI) 173 | """, incr = 300, message = 'MCM')) 174 | #ENDINLINE 175 | #EQUATIONS 176 | {1.} O = O3 : 5.6e-34*N2*(TEMP/300)**(-2.6)*O2+6.0e-34*O2*(TEMP/300)**(-2.6)*O2 ; 177 | {2.} O + O3 = DUMMY : 8.0e-12*EXP(-2060/TEMP) ; 178 | {3.} O + NO = NO2 : KMT01 ; 179 | {4.} O + NO2 = NO : 5.5e-12*EXP(188/TEMP) ; 180 | {5.} O + NO2 = NO3 : KMT02 ; 181 | {6.} O1D = O : 3.2e-11*EXP(67/TEMP)*O2+2.0e-11*EXP(130/TEMP)*N2 ; 182 | {7.} NO + O3 = NO2 : 1.4e-12*EXP(-1310/TEMP) ; 183 | {8.} NO2 + O3 = NO3 : 1.4e-13*EXP(-2470/TEMP) ; 184 | {9.} NO + NO = NO2 + NO2 : 3.3e-39*EXP(530/TEMP)*O2 ; 185 | {10.} NO + NO3 = NO2 + NO2 : 1.8e-11*EXP(110/TEMP) ; 186 | {11.} NO2 + NO3 = NO + NO2 : 4.50e-14*EXP(-1260/TEMP) ; 187 | {12.} NO2 + NO3 = N2O5 : KMT03 ; 188 | {13.} O1D = OH + OH : 2.14e-10*H2O ; 189 | {14.} OH + O3 = HO2 : 1.70e-12*EXP(-940/TEMP) ; 190 | {15.} OH = HO2 : 7.7e-12*EXP(-2100/TEMP)*H2 ; 191 | {16.} OH + CO = HO2 : KMT05 ; 192 | {17.} OH + H2O2 = HO2 : 2.9e-12*EXP(-160/TEMP) ; 193 | {18.} HO2 + O3 = OH : 2.03e-16*(TEMP/300)**(4.57)*EXP(693/TEMP) ; 194 | {19.} OH + HO2 = DUMMY : 4.8e-11*EXP(250/TEMP) ; 195 | {20.} HO2 + HO2 = H2O2 : 2.20e-13*KMT06*EXP(600/TEMP)+1.90e-33*M*KMT06*EXP(980/TEMP) ; 196 | {21.} OH + NO = HONO : KMT07 ; 197 | {22.} OH + NO2 = HNO3 : KMT08 ; 198 | {23.} OH + NO3 = HO2 + NO2 : 2.0e-11 ; 199 | {24.} HO2 + NO = OH + NO2 : 3.45e-12*EXP(270/TEMP) ; 200 | {25.} HO2 + NO2 = HO2NO2 : KMT09 ; 201 | {26.} OH + HO2NO2 = NO2 : 3.2e-13*EXP(690/TEMP)*1.0 ; 202 | {27.} HO2 + NO3 = OH + NO2 : 4.0e-12 ; 203 | {28.} OH + HONO = NO2 : 2.5e-12*EXP(260/TEMP) ; 204 | {29.} OH + HNO3 = NO3 : KMT11 ; 205 | {30.} O + SO2 = SO3 : 4.0e-32*EXP(-1000/TEMP)*M ; 206 | {31.} OH + SO2 = HSO3 : KMT12 ; 207 | {32.} HSO3 = HO2 + SO3 : 1.3e-12*EXP(-330/TEMP)*O2 ; 208 | {33.} HNO3 = NA : 6.00e-06 ; 209 | {34.} N2O5 = NA + NA : 4.00e-04 ; 210 | {35.} SO3 = SA : 1.20e-15*H2O ; 211 | {36.} O3 = O1D : MCMJ(1, THETA) ; 212 | {37.} O3 = O : MCMJ(2, THETA) ; 213 | {38.} H2O2 = OH + OH : MCMJ(3, THETA) ; 214 | {39.} NO2 = NO + O : MCMJ(4, THETA) ; 215 | {40.} NO3 = NO : MCMJ(5, THETA) ; 216 | {41.} NO3 = NO2 + O : MCMJ(6, THETA) ; 217 | {42.} HONO = OH + NO : MCMJ(7, THETA) ; 218 | {43.} HNO3 = OH + NO2 : MCMJ(8, THETA) ; 219 | {44.} N2O5 = NO2 + NO3 : KMT04 ; 220 | {45.} HO2NO2 = HO2 + NO2 : KMT10 ; 221 | {46.} CL + CH4 = CH3O2 : 6.6e-12*EXP(-1240/TEMP) ; 222 | {47.} OH + CH4 = CH3O2 : 1.85e-12*EXP(-1690/TEMP) ; 223 | {48.} CH3O2 + HO2 = CH3OOH : 3.8e-13*EXP(780/TEMP)*(1-1/(1+498*EXP(-1160/TEMP))) ; 224 | {49.} CH3O2 + HO2 = HCHO : 3.8e-13*EXP(780/TEMP)*(1/(1+498*EXP(-1160/TEMP))) ; 225 | {50.} CH3O2 + NO = CH3NO3 : 2.3e-12*EXP(360/TEMP)*0.001 ; 226 | {51.} CH3O2 + NO = CH3O + NO2 : 2.3e-12*EXP(360/TEMP)*0.999 ; 227 | {52.} CH3O2 + NO2 = CH3O2NO2 : KMT13 ; 228 | {53.} CH3O2 + NO3 = CH3O + NO2 : 1.2e-12 ; 229 | {54.} CH3O2 = CH3O : 2*KCH3O2*RO2*7.18*EXP(-885/TEMP) ; 230 | {55.} CH3O2 = CH3OH : 2*KCH3O2*RO2*0.5*(1-7.18*EXP(-885/TEMP)) ; 231 | {56.} CH3O2 = HCHO : 2*KCH3O2*RO2*0.5*(1-7.18*EXP(-885/TEMP)) ; 232 | {57.} CH3OOH = CH3O + OH : MCMJ(41, THETA) ; 233 | {58.} OH + CH3OOH = CH3O2 : 5.3e-12*EXP(190/TEMP)*0.6 ; 234 | {59.} OH + CH3OOH = HCHO + OH : 5.3e-12*EXP(190/TEMP)*0.4 ; 235 | {60.} HCHO = CO + HO2 + HO2 : MCMJ(11, THETA) ; 236 | {61.} HCHO = {H2 +} CO : MCMJ(12, THETA) ; 237 | {62.} NO3 + HCHO = HNO3 + CO + HO2 : 5.5e-16 ; 238 | {63.} OH + HCHO = HO2 + CO : 5.4e-12*EXP(135/TEMP) ; 239 | {64.} CH3NO3 = CH3O + NO2 : MCMJ(51, THETA) ; 240 | {65.} OH + CH3NO3 = HCHO + NO2 : 4.0e-13*EXP(-845/TEMP) ; 241 | {66.} CH3O = HCHO + HO2 : 7.2e-14*EXP(-1080/TEMP)*O2 ; 242 | {67.} CH3O2NO2 = CH3O2 + NO2 : KMT14 ; 243 | {68.} CH3OH + OH = HO2 + HCHO : 2.85e-12*EXP(-345/TEMP) ; 244 | 245 | -------------------------------------------------------------------------------- /pykpp/models/melchior1_soa_cpx.eqn: -------------------------------------------------------------------------------- 1 | #EQUATIONS 2 | <1> APINEN + NO3 = APINO3 + 0.8 BiBmP : (1.19e-12) * exp(-(-490) / TEMP) {k(T)=Aexp(-B/T),A=1.19e-12,B=-490}; 3 | <2> BPINEN + NO3 = APINO3 + 0.8 BiBmP : (2.51e-12) {k=2.51e-12}; 4 | <3> OCIMEN + NO3 = APINO3 + 0.7 BiA0D + 0.075 BiA1D : 4.3e-9 / TEMP {k(1/T)=A/T,A=4.3e-9}; 5 | <4> APINEN + OH = APIOH + 0.30 BiA0D + 0.17 BiA1D + 0.10 BiA2D : (1.21e-11) * exp(-(-444) / TEMP) {k(T)=Aexp(-B/T),A=1.21e-11,B=-444}; 6 | <5> BPINEN + OH = APIOH + 0.07 BiA0D + 0.08 BiA1D + 0.06 BiA2D : (2.38e-11) * exp(-(-357) / TEMP) {k(T)=Aexp(-B/T),A=2.38e-11,B=-357}; 7 | <6> OCIMEN + OH = APIOH + 0.70 BiA0D + 0.075 BiA1D : 5.1e-8 / TEMP {k(1/T)=A/T,A=5.1e-8}; 8 | <7> HUMULE + OH = APIOH + 0.74 BiBmP + 0.26 BiBIP : (2.93e-10) {k=2.93e-10}; 9 | <8> APINEN + O3 = 0.65 CH3CHO + 0.53 CH3COE + 0.14 CO + 0.2 C2H5O2 + 0.42 CH3CHX + 0.85 OH + 0.1 HO2 + 0.18 BiA0D + 0.16 BiA1D + 0.05 BiA2D : (1.01e-15) * exp(-(732) / TEMP) {k(T)=Aexp(-B/T),A=1.01e-15,B=732}; 10 | <9> BPINEN + O3 = 0.65 CH3CHO + 0.53 CH3COE + 0.14 CO + 0.2 C2H5O2 + 0.42 CH3CHX + 0.85 OH + 0.1 HO2 + 0.09 BiA0D + 0.13 BiA1D + 0.04 BiA2D : (1.5e-17) {k=1.5e-17}; 11 | <10> OCIMEN + O3 = 0.65 CH3CHO + 0.53 CH3COE + 0.14 CO + 0.2 C2H5O2 + 0.42 CH3CHX + 0.85 OH + 0.1 HO2 + 0.5 BiA0D + 0.055 BiA1D : 7.5e-14 / TEMP {k(1/T)=A/T,A=7.5e-14}; 12 | <11> LIMONE + O3 = 0.65 CH3CHO + 0.53 CH3COE + 0.14 CO + 0.2 C2H5O2 + 0.42 CH3CHX + 0.85 OH + 0.1 HO2 + 0.09 BiA0D + 0.10 BiA1D : (2e-16) {k=2e-16}; 13 | <12> C5H8 + OH = 0.232 ISOPA1 + 0.0288 ISOPA2 + RO2IP1 : (2.55e-11) * exp(-(-410) / TEMP) {k(T)=Aexp(-B/T),A=2.55e-11,B=-410}; 14 | <13> TOL + OH = OH + 0.004 AnA0D + 0.001 AnA1D + 0.084 AnBmP + 0.013 AnBIP : (1.81e-12) * exp(-(-355) / TEMP) {k(T)=Aexp(-B/T),A=1.81e-12,B=-355}; 15 | <14> TMB + OH = OH + 0.002 AnA0D + 0.002 AnA1D + 0.001 AnA2D + 0.088 AnBmP + 0.006 AnBIP : 9.80e-9 / TEMP {k(1/T)=A/T,A=9.80e-9}; 16 | <15> NC4H10 + OH = SECC4H + 0.07 AnBmP: (1.36e-12) * exp(-(-190) / TEMP) * (300. / TEMP)**(-2) {k(T)=Aexp(-B/T)(300/T)**N,A=1.36e-12,B=-190,N=-2}; 17 | <16> OXYL + OH = OXYL1 : (1.37e-11) {k=1.37e-11}; 18 | <17> AnA2D = X : (1e-18) {k=1e-18}; 19 | <18> AnA1D = X : (1e-18) {k=1e-18}; 20 | <19> AnA0D = X : (1e-18) {k=1e-18}; 21 | <20> AnBmP = X : (1e-18) {k=1e-18}; 22 | <21> AnBIP = X : (1e-18) {k=1e-18}; 23 | <22> BiA2D = X : (1e-18) {k=1e-18}; 24 | <23> BiA1D = X : (1e-18) {k=1e-18}; 25 | <24> BiA0D = X : (1e-18) {k=1e-18}; 26 | <25> BiBmP = X : (1e-18) {k=1e-18}; 27 | <26> ISOPA1 = X : (1e-18) {k=1e-18}; 28 | <27> ISOPA2 = X : (1e-18) {k=1e-18}; 29 | <28> AnA2DAQ = X : (1e-18) {k=1e-18}; 30 | <29> AnA1DAQ = X : (1e-18) {k=1e-18}; 31 | <30> AnA0DAQ = X : (1e-18) {k=1e-18}; 32 | <31> AnBmPAQ = X : (1e-18) {k=1e-18}; 33 | <32> AnBIPAQ = X : (1e-18) {k=1e-18}; 34 | <33> BiA2DAQ = X : (1e-18) {k=1e-18}; 35 | <34> BiA1DAQ = X : (1e-18) {k=1e-18}; 36 | <35> BiA0DAQ = X : (1e-18) {k=1e-18}; 37 | <36> BiBmPAQ = X : (1e-18) {k=1e-18}; 38 | <37> ISOPA1AQ = X : (1e-18) {k=1e-18}; 39 | <38> ISOPA2AQ = X : (1e-18) {k=1e-18}; 40 | -------------------------------------------------------------------------------- /pykpp/models/melchior2.eqn: -------------------------------------------------------------------------------- 1 | #EQUATIONS 2 | <1> O3 + NO = NO2 : (1.8e-12) * exp(-(1370) / TEMP) {k(T)=Aexp(-B/T),A=1.8e-12,B=1370}; 3 | <2> O3 + NO2 = NO3 : (1.2e-13) * exp(-(2450) / TEMP) {k(T)=Aexp(-B/T),A=1.2e-13,B=2450}; 4 | <3> O3 + OH = HO2 : (1.9e-12) * exp(-(1000) / TEMP) {k(T)=Aexp(-B/T),A=1.9e-12,B=1000}; 5 | <4> O3 + HO2 = OH : (1.4e-14) * exp(-(600) / TEMP) {k(T)=Aexp(-B/T),A=1.4e-14,B=600}; 6 | <5> NO + HO2 = OH + NO2 : (3.7e-12) * exp(-(-240) / TEMP) {k(T)=Aexp(-B/T),A=3.7e-12,B=-240}; 7 | <6> NO2 + OH + M = HNO3 : CHIMERE_MTROE(3.4e-30, 0, 3.2, 4.77e-11, 0, 1.4, 0.30) {k(T,M)=mtroe(3.4e-30,0,3.2,4.77e-11,0,1.4,0.30)}; 8 | <7> HO2 + OH = H2O : (4.8e-11) * exp(-(-250) / TEMP) {k(T)=Aexp(-B/T),A=4.8e-11,B=-250}; 9 | <8> H2O2 + OH = HO2 : (2.9e-12) * exp(-(160) / TEMP) {k(T)=Aexp(-B/T),A=2.9e-12,B=160}; 10 | <9> HNO3 + OH = NO3 : (5.5e-15) * exp(-(-985) / TEMP) {k(T)=Aexp(-B/T),A=5.5e-15,B=-985}; 11 | <10> CO + OH = HO2 + CO2: (2e-13) * exp(-(0) / TEMP) * (300. / TEMP)**(1) {k(T)=Aexp(-B/T)(300/T)**N,A=2e-13,B=0,N=1}; 12 | <11> HO2 + HO2 = H2O2 : (2.2e-13) * exp(-(-740) / TEMP) {k(T)=Aexp(-B/T),A=2.2e-13,B=-740}; 13 | <12> HO2 + HO2 + H2O = H2O2 : (4.52e-34) * exp(-(-2827) / TEMP) {k(T)=Aexp(-B/T),A=4.52e-34,B=-2827}; 14 | <13> NO3 + HO2 = NO2 + OH : (4e-12) {k=4e-12}; 15 | <14> NO3 + H2O2 = HNO3 + HO2 : (2e-15) {k=2e-15}; 16 | <15> NO3 + NO = 2 NO2 : (1.8e-11) * exp(-(-110) / TEMP) {k(T)=Aexp(-B/T),A=1.8e-11,B=-110}; 17 | <16> NO2 + NO3 = NO + NO2 : (4.5e-14) * exp(-(1260) / TEMP) {k(T)=Aexp(-B/T),A=4.5e-14,B=1260}; 18 | <17> NO2 + NO3 + M = N2O5 : CHIMERE_TROE(2.7e-30, 0, 3.4, 2e-12, 0, -0.2, 0.33) {k(T,M)=troe(2.7e-30,0,3.4,2e-12,0,-0.2,0.33)}; 19 | <18> N2O5 + M = NO3 + NO2 : CHIMERE_TROE(1e-3, 11000, 3.5, 9.7e14, 11080, -0.1, 0.33) {k(T,M)=troe(1e-3,11000,3.5,9.7e14,11080,-0.1,0.33)}; 20 | <19> NO + OH + M = HONO : CHIMERE_TROE(7.e-31, 0, 2.6, 1.5e-11, 0, 0.5, 0.6) {k(T,M)=troe(7.e-31,0,2.6,1.5e-11,0,0.5,0.6)}; 21 | <20> HONO + OH = NO2 : (1.8e-11) * exp(-(390) / TEMP) {k(T)=Aexp(-B/T),A=1.8e-11,B=390}; 22 | <21> NO + NO + O2 = 2 NO2 : (3.30E-39) * exp(-(-530.0) / TEMP) {k(T)=Aexp(-B/T),A=3.30E-39,B=-530.0}; 23 | <22> NO2 = HONO + NO2 : 0. {ks=0.5 depo(NO2)}; 24 | <23> SO2 + CH3O2 = H2SO4 + HCHO + HO2 : (4e-17) {k=4e-17}; 25 | <24> SO2 + OH + M = H2SO4 + HO2 : CHIMERE_TROE(4e-31, 0, 3.3, 2e-12, 0, 0, 0.45) {k(T,M)=troe(4e-31,0,3.3,2e-12,0,0,0.45)}; 26 | <25> obio + NO = 0.86 NO2 + 0.78 HO2 + 0.14 ISNI : (1.6e-11) * exp(-(180) / TEMP) {k(T)=Aexp(-B/T),A=1.6e-11,B=180}; 27 | <26> obio + HO2 = obioH : (2.7e-13) * exp(-(-1000) / TEMP) {k(T)=Aexp(-B/T),A=2.7e-13,B=-1000}; 28 | <27> obio + NO3 = NO2 + HO2 : (1.2e-12) {k=1.2e-12}; 29 | <28> CH3O2 + obio = 0.8 HO2 + 0.5 HCHO : (2.44e-11) * exp(-(223) / TEMP) {k(T)=Aexp(-B/T),A=2.44e-11,B=223}; 30 | <29> CH3COO + obio = 0.5 HCHO + 1.5 HO2 + 0.7 CO2 : (1.18e-11) * exp(-(127) / TEMP) {k(T)=Aexp(-B/T),A=1.18e-11,B=127}; 31 | <30> obioH + OH = OH : (8e-11) {k=8e-11}; 32 | <31> oRO2 + NO = NO2 + HO2 : (4e-12) {k=4e-12}; 33 | <32> oRO2 + HO2 = oROOH : (2.7e-13) * exp(-(-1000) / TEMP) {k(T)=Aexp(-B/T),A=2.7e-13,B=-1000}; 34 | <33> oRO2 + oRO2 = 1.3 HO2 : (6.4e-14) {k=6.4e-14}; 35 | <34> oRO2 + NO3 = NO2 + HO2 : (1.2e-12) {k=1.2e-12}; 36 | <35> CH3O2 + oRO2 = 0.65 HCHO + 0.8 HO2 + 0.35 CH3OH : (1.5e-13) * exp(-(-220) / TEMP) {k(T)=Aexp(-B/T),A=1.5e-13,B=-220}; 37 | <36> CH3COO + oRO2 = 0.8 CH3O2 + 0.8 CO2 + 0.8 HO2 : (8.6e-13) * exp(-(-260) / TEMP) {k(T)=Aexp(-B/T),A=8.6e-13,B=-260}; 38 | <37> oROOH + OH = 0.8 OH + 0.2 oRO2 : (4.35e-12) * exp(-(-455) / TEMP) {k(T)=Aexp(-B/T),A=4.35e-12,B=-455}; 39 | <38> MAC + OH = 0.5 CH3COE + 0.5 CO2 + 0.5 oPAN : (1.86e-11) * exp(-(-175) / TEMP) {k(T)=Aexp(-B/T),A=1.86e-11,B=-175}; 40 | <39> oPAN + NO = NO2 + HO2 : (1.4e-11) {k=1.4e-11}; 41 | <40> oPAN + NO3 = NO2 + HO2 : (4e-12) {k=4e-12}; 42 | <41> oPAN + HO2 = PANH : (2.7e-13) * exp(-(-1000) / TEMP) {k(T)=Aexp(-B/T),A=2.7e-13,B=-1000}; 43 | <42> PANH + OH = 0.2 oPAN : (1.64e-11) {k=1.64e-11}; 44 | <43> oPAN + CH3O2 = HCHO + 0.5 HO2 : (7.9e-12) * exp(-(-140) / TEMP) {k(T)=Aexp(-B/T),A=7.9e-12,B=-140}; 45 | <44> oPAN + CH3COO = CH3O2 + CO2 + HO2 : (5.6e-12) * exp(-(-530) / TEMP) {k(T)=Aexp(-B/T),A=5.6e-12,B=-530}; 46 | <45> oPAN + NO2 + M = toPAN : CHIMERE_TROE(2.7e-28, 0, 7.1, 1.2e-11, 0, 0.9, 0.3) {k(T,M)=troe(2.7e-28,0,7.1,1.2e-11,0,0.9,0.3)}; 47 | <46> toPAN + M = oPAN + NO2 : CHIMERE_TROE(4.9e-3, 12100, 0, 5.4e16, 13830, 0, 0.3) {k(T,M)=troe(4.9e-3,12100,0,5.4e16,13830,0,0.3)}; 48 | <47> toPAN + OH = NO3 + CO2 : (3.25e-13) * exp(-(-500) / TEMP) {k(T)=Aexp(-B/T),A=3.25e-13,B=-500}; 49 | <48> C2H4 + NO3 = 0.5 CARNIT + HCHO + oRN1 : (2e-16) {k=2e-16}; 50 | <49> C3H6 + NO3 = 0.5 CARNIT + 1.5 HCHO + 0.5 CH3CHO + 0.5 HO2 + oRN1 : (9.45e-15) {k=9.45e-15}; 51 | <50> ISNI + OH = oRN1 + 0.95 CH3CHO + 0.475 CH3COE + 0.475 MGLYOX + 0.05 ISNI + 0.05 HO2 : (3.4e-11) {k=3.4e-11}; 52 | <51> C5H8 + NO3 = oRN1 + 0.85 ISNI + 0.1 MAC + 0.05 MVK + 0.15 HCHO + 0.8 HO2 : (7.8e-13) {k=7.8e-13}; 53 | <52> oRN1 + NO = 1.5 NO2 : (4e-11) {k=4e-11}; 54 | <53> oRN1 + NO3 = 1.5 NO2 : (1.2e-12) {k=1.2e-12}; 55 | <54> oRN1 + HO2 = X : (2.7e-13) * exp(-(-1000) / TEMP) {k(T)=Aexp(-B/T),A=2.7e-13,B=-1000}; 56 | <55> CH4 + OH = CH3O2 : (2.3e-12) * exp(-(1765) / TEMP) {k(T)=Aexp(-B/T),A=2.3e-12,B=1765}; 57 | <56> C2H6 + OH = CH3CHO + oRO2 : (7.9e-12) * exp(-(1030) / TEMP) {k(T)=Aexp(-B/T),A=7.9e-12,B=1030}; 58 | <57> C2H4 + OH + M = 2 HCHO + oRO2 : CHIMERE_TROE(7e-29, 0, 3.1, 9e-12, 0, 0, 0.7) {k(T,M)=troe(7e-29,0,3.1,9e-12,0,0,0.7)}; 59 | <58> C3H6 + OH + M = HCHO + CH3CHO + oRO2 : CHIMERE_TROE(8e-27, 0, 3.5, 3e-11, 0, 0, 0.5) {k(T,M)=troe(8e-27,0,3.5,3e-11,0,0,0.5)}; 60 | <59> HCHO + OH = CO + HO2 : (8.6e-12) * exp(-(-20) / TEMP) {k(T)=Aexp(-B/T),A=8.6e-12,B=-20}; 61 | <60> CH3CHO + OH = CH3COO : (5.6e-12) * exp(-(-310) / TEMP) {k(T)=Aexp(-B/T),A=5.6e-12,B=-310}; 62 | <61> MEMALD + OH = GLYOX + MGLYOX + oRO2 : (5.6e-11) {k=5.6e-11}; 63 | <62> CH3COE + OH = CH3COY + oRO2: (2.92e-13) * exp(-(-414) / TEMP) * (300. / TEMP)**(-2) {k(T)=Aexp(-B/T)(300/T)**N,A=2.92e-13,B=-414,N=-2}; 64 | <63> GLYOX + OH = 2 CO + HO2 : (1.1e-11) {k=1.1e-11}; 65 | <64> MGLYOX + OH = CH3COO + CO : (1.5e-11) {k=1.5e-11}; 66 | <65> MVK + OH = 0.266 MGLYOX + 0.266 HCHO + 0.684 CH3CHO + 0.684 CH3COO + 0.05 ISNI + 0.95 oRO2 : (4.1e-12) * exp(-(-453) / TEMP) {k(T)=Aexp(-B/T),A=4.1e-12,B=-453}; 67 | <66> CH3O2H + OH = CH3O2 : (1.9e-12) * exp(-(-190) / TEMP) {k(T)=Aexp(-B/T),A=1.9e-12,B=-190}; 68 | <67> PPA + OH = CH3COO : (1.9e-12) * exp(-(-190) / TEMP) {k(T)=Aexp(-B/T),A=1.9e-12,B=-190}; 69 | <68> CH3O2H + OH = HCHO + OH : (1.e-12) * exp(-(-190) / TEMP) {k(T)=Aexp(-B/T),A=1.e-12,B=-190}; 70 | <69> HCHO + NO3 = CO + HNO3 + HO2 : (5.8e-16) {k=5.8e-16}; 71 | <70> CH3CHO + NO3 = CH3COO + HNO3 : (2.8e-15) {k=2.8e-15}; 72 | <71> CH3O2 + NO3 = HCHO + HO2 + NO2 : (1.2e-12) {k=1.2e-12}; 73 | <72> CH3COO + NO3 = CH3O2 + NO2 + CO2 : (4e-12) {k=4e-12}; 74 | <73> C2H4 + O3 = HCHO + 0.12 HO2 + 0.13 H2 + 0.44 CO : (9.1e-15) * exp(-(2580) / TEMP) {k(T)=Aexp(-B/T),A=9.1e-15,B=2580}; 75 | <74> C3H6 + O3 = 0.53 HCHO + 0.5 CH3CHO + 0.31 CH3O2 + 0.28 HO2 + 0.15 OH + 0.065 H2 + 0.4 CO + 0.7 CH4 : (5.5e-15) * exp(-(1880) / TEMP) {k(T)=Aexp(-B/T),A=5.5e-15,B=1880}; 76 | <75> C5H8 + O3 = 0.67 MAC + 0.26 MVK + 0.55 OH + 0.07 C3H6 + 0.8 HCHO + 0.06 HO2 + 0.05 CO + 0.3 O3 : (1.2e-14) * exp(-(2013) / TEMP) {k(T)=Aexp(-B/T),A=1.2e-14,B=2013}; 77 | <76> MAC + O3 = 0.8 MGLYOX + 0.7 HCHO + 0.215 OH + 0.275 HO2 + 0.2 CO + 0.2 O3 : (5.3e-15) * exp(-(2520) / TEMP) {k(T)=Aexp(-B/T),A=5.3e-15,B=2520}; 78 | <77> MVK + O3 = 0.82 MGLYOX + 0.8 HCHO + 0.04 CH3CHO + 0.08 OH + 0.06 HO2 + 0.05 CO + 0.2 O3 : (4.3e-15) * exp(-(2016) / TEMP) {k(T)=Aexp(-B/T),A=4.3e-15,B=2016}; 79 | <78> CH3O2 + NO = HCHO + NO2 + HO2 : (4.2e-12) * exp(-(-180) / TEMP) {k(T)=Aexp(-B/T),A=4.2e-12,B=-180}; 80 | <79> CH3COO + NO = CH3O2 + NO2 + CO2 : (2e-11) {k=2e-11}; 81 | <80> CH3O2 + HO2 = CH3O2H : (4.1e-13) * exp(-(-790) / TEMP) {k(T)=Aexp(-B/T),A=4.1e-13,B=-790}; 82 | <81> CH3COO + HO2 = 0.67 PPA + 0.33 O3 : (4.3e-13) * exp(-(-1040) / TEMP) {k(T)=Aexp(-B/T),A=4.3e-13,B=-1040}; 83 | <82> CH3O2 + CH3O2 = 1.35 HCHO + 0.7 HO2 : (1.13e-13) * exp(-(-356) / TEMP) {k(T)=Aexp(-B/T),A=1.13e-13,B=-356}; 84 | <83> CH3COO + CH3O2 = 0.5 CH3O2 + 0.5 CO2 + HCHO + 0.5 HO2 : (3.34e-12) * exp(-(-400) / TEMP) {k(T)=Aexp(-B/T),A=3.34e-12,B=-400}; 85 | <84> CH3COO + CH3COO = 2. CH3O2 + 2. CO2 : (2.8e-12) * exp(-(-530) / TEMP) {k(T)=Aexp(-B/T),A=2.8e-12,B=-530}; 86 | <85> CH3COO + NO2 + M = PAN : CHIMERE_TROE(2.7e-28, 0, 7.1, 1.2e-11, 0, 0.9, 0.3) {k(T,M)=troe(2.7e-28,0,7.1,1.2e-11,0,0.9,0.3)}; 87 | <86> PAN + M = CH3COO + NO2 : CHIMERE_TROE(4.9e-3, 12100, 0, 5.4e16, 13830, 0, 0.3) {k(T,M)=troe(4.9e-3,12100,0,5.4e16,13830,0,0.3)}; 88 | <87> PAN + OH = HCHO + NO3 + CO2 : (9.5e-13) * exp(-(650) / TEMP) {k(T)=Aexp(-B/T),A=9.5e-13,B=650}; 89 | <88> CARNIT + OH = CH3CHO + CO + NO2 : (5.6e-12) * exp(-(-310) / TEMP) {k(T)=Aexp(-B/T),A=5.6e-12,B=-310}; 90 | <89> O3 = 2 OH : CHIMERE_JO3(TUV_J5pt0('O2 -> O + O', THETA)) {J(T,Z,H2O)=photorate(O3)}; 91 | <90> NO2 = NO + O3 : TUV_J5pt0('NO2 -> NO + O(3P)', THETA) {J(Z)=photorate(NO2)}; 92 | <91> NO3 = NO : TUV_J5pt0('NO3 -> NO + O2', THETA) {J(Z)=photorate(NO3-1)}; 93 | <92> NO3 = NO2 + O3 : TUV_J5pt0('NO3 -> NO2 + O(3P)', THETA) {J(Z)=photorate(NO3-2)}; 94 | <93> H2O2 = 2 OH : TUV_J5pt0('H2O2 -> 2 OH', THETA) {J(Z)=photorate(H2O2)}; 95 | <94> HNO3 = NO2 + OH : TUV_J5pt0('HNO3 -> OH + NO2', THETA) {J(Z)=photorate(HNO3)}; 96 | <95> HONO = NO + OH : TUV_J5pt0('HNO2 -> OH + NO', THETA) {J(Z)=photorate(HONO)}; 97 | <96> HCHO = CO + 2 HO2 : TUV_J5pt0('CH2O -> H + HCO', THETA) {J(Z)=photorate(HCHO-1)}; 98 | <97> HCHO = CO + H2 : TUV_J5pt0('CH2O -> H2 + CO', THETA) {J(Z)=photorate(HCHO-2)}; 99 | <98> CH3CHO = CH3O2 + HO2 + CO : TUV_J5pt0('CH3CHO -> CH3 + HCO', THETA) + TUV_J5pt0('CH3CHO -> CH3CO + H', THETA) {J(Z)=photorate(CH3CHO)}; 100 | <99> CH3COY = 2 CH3COO : TUV_J5pt0('CH3COCOCH3 -> Products', THETA) {J(Z)=photorate(CH3COY) is this right?}; 101 | <100> MGLYOX = CH3COO + HO2 + CO : TUV_J5pt0('CH3COCHO -> CH3CO + HCO', THETA) {J(Z)=photorate(MGLYOX)}; 102 | <101> GLYOX = 0.6 HO2 + 2 CO + 1.4 H2 : TUV_J5pt0('CHOCHO -> HCO + HCO', THETA) + TUV_J5pt0('CHOCHO -> CH2O + CO', THETA) {J(Z)=photorate(GLYOX)}; 103 | <102> MEMALD = 0.5 MVK + 0.5 MALEIC + 0.5 oPAN + 0.5 HCHO + 0.5 HO2 : TUV_J5pt0('CH3COCO(OH) -> Products', THETA) {J(Z)=photorate(MEMALD) -- pretty sure this is wrong?}; 104 | <103> CH3COE = CH3COO + CH3CHO + oRO2 : TUV_J5pt0('CH3COCH2CH3 -> CH3CO + CH2CH3', THETA) {J(Z)=photorate(CH3COE)}; 105 | <104> N2O5 = NO2 + NO3 : TUV_J5pt0('N2O5 -> NO3 + NO + O(3P)', THETA) + TUV_J5pt0('N2O5 -> NO3 + NO2', THETA) {J(Z)=photorate(N2O5)}; 106 | <105> CH3O2H = HCHO + OH + HO2 : TUV_J5pt0('CH3OOH -> CH3O + OH', THETA) {J(Z)=photorate(CH3O2H)}; 107 | <106> PPA = CH3O2 + CO2 + OH : TUV_J5pt0('CH3CO(OOH) -> Products', THETA) {J(Z)=photorate(PPA)}; 108 | <107> PAN = CH3COO + NO2 : TUV_J5pt0('CH3CO(OONO2) -> CH3CO(OO) + NO2', THETA) + TUV_J5pt0('CH3CO(OONO2) -> CH3CO(O) + NO3', THETA) {J(Z)=photorate(PAN)}; 109 | <108> PANH = OH + HO2 : TUV_J5pt0('CH3CH2CO(OONO2) -> CH3CH2CO(OO) + NO2', THETA) + TUV_J5pt0('CH3CH2CO(OONO2) -> CH3CH2CO(O) + NO3', THETA) {J(Z)=photorate(PANH)}; 110 | <109> oROOH = OH + HO2 : TUV_J5pt0('CH3OOH -> CH3O + OH', THETA) {J(Z)=photorate(oROOH)}; 111 | <110> obioH = OH + HO2 : TUV_J5pt0('CH3OOH -> CH3O + OH', THETA) {J(Z)=photorate(obioH)}; 112 | -------------------------------------------------------------------------------- /pykpp/models/melchior2_soa_cpx.eqn: -------------------------------------------------------------------------------- 1 | #EQUATIONS 2 | <1> APINEN + NO3 = CH3CHO + CH3COE + oRN1 + 0.8 BiBmP : (1.19e-12) * exp(-(-490) / TEMP) {k(T)=Aexp(-B/T),A=1.19e-12,B=-490}; 3 | <2> BPINEN + NO3 = CH3CHO + CH3COE + oRN1 + 0.8 BiBmP : (2.51e-12) {k=2.51e-12}; 4 | <3> OCIMEN + NO3 = CH3CHO + CH3COE + oRN1 + 0.7 BiA0D + 0.075 BiA1D : 4.30e-9 / TEMP {k(1/T)=A/T,A=4.30e-9}; 5 | <4> APINEN + OH = 0.8 CH3CHO + 0.8 CH3COE + obio+0.30 BiA0D + 0.17 BiA1D + 0.10 BiA2D : (1.21e-11) * exp(-(-444) / TEMP) {k(T)=Aexp(-B/T),A=1.21e-11,B=-444}; 6 | <5> BPINEN + OH = 0.8 CH3CHO + 0.8 CH3COE + obio+0.07 BiA0D + 0.08 BiA1D + 0.06 BiA2D : (2.38e-11) * exp(-(-357) / TEMP) {k(T)=Aexp(-B/T),A=2.38e-11,B=-357}; 7 | <6> OCIMEN + OH = 0.8 CH3CHO + 0.8 CH3COE + obio+0.70 BiA0D + 0.075 BiA1D : 5.1e-8 / TEMP {k(1/T)=A/T,A=5.1e-8}; 8 | <7> HUMULE + OH = 0.8 CH3CHO + 0.8 CH3COE + obio+0.74 BiBmP + 0.26 BiBIP : (2.93e-10) {k=2.93e-10}; 9 | <8> C5H8 + OH = 0.232 ISOPA1 + 0.0288 ISOPA2 + 0.32 MAC + 0.42 MVK + 0.74 HCHO + obio : (2.55e-11) * exp(-(-410) / TEMP) {k(T)=Aexp(-B/T),A=2.55e-11,B=-410}; 10 | <9> APINEN + O3 = 1.27 CH3CHO + 0.53 CH3COE + 0.14 CO + 0.62 oRO2 + 0.42 HCHO + 0.85 OH + 0.1 HO2 + 0.18 BiA0D + 0.16 BiA1D + 0.05 BiA2D : (1.01e-15) * exp(-(732) / TEMP) {k(T)=Aexp(-B/T),A=1.01e-15,B=732}; 11 | <10> BPINEN + O3 = 1.27 CH3CHO + 0.53 CH3COE + 0.14 CO + 0.62 oRO2 + 0.42 HCHO + 0.85 OH + 0.1 HO2 + 0.09 BiA0D + 0.13 BiA1D + 0.04 BiA2D : (1.5e-17) {k=1.5e-17}; 12 | <11> OCIMEN + O3 = 1.27 CH3CHO + 0.53 CH3COE + 0.14 CO + 0.62 oRO2 + 0.42 HCHO + 0.85 OH + 0.1 HO2 + 0.50 BiA0D + 0.055 BiA1D : 7.5e-14 / TEMP {k(1/T)=A/T,A=7.5e-14}; 13 | <12> LIMONE + O3 = 1.27 CH3CHO + 0.53 CH3COE + 0.14 CO + 0.62 oRO2 + 0.42 HCHO + 0.85 OH + 0.1 HO2 + 0.09 BiA0D + 0.10 BiA1D : (2e-16) {k=2e-16}; 14 | <13> TOL + OH = OH + 0.004 AnA0D + 0.001 AnA1D + 0.084 AnBmP + 0.013 AnBIP : (1.81e-12) * exp(-(-355) / TEMP) {k(T)=Aexp(-B/T),A=1.81e-12,B=-355}; 15 | <14> TMB + OH = OH + 0.002 AnA0D + 0.002 AnA1D + 0.001 AnA2D + 0.088 AnBmP + 0.006 AnBIP : 9.80e-9 / TEMP {k(1/T)=A/T,A=9.80e-9}; 16 | <15> NC4H10 + OH = 0.9 CH3COE + 0.1 CH3CHO + 0.1 CH3COO + 0.9 oRO2 + 0.07 AnBmP: (1.36e-12) * exp(-(-190) / TEMP) * (300. / TEMP)**(-2) {k(T)=Aexp(-B/T)(300/T)**N,A=1.36e-12,B=-190,N=-2}; 17 | <16> OXYL + OH = MEMALD + MGLYOX + oRO2 : (1.37e-11) {k=1.37e-11}; 18 | -------------------------------------------------------------------------------- /pykpp/models/prep/__init__.py: -------------------------------------------------------------------------------- 1 | def help(): 2 | """ 3 | This folder keeps mechanism translators that should operate 4 | as stand-alone scripts. They are kept here as a central 5 | repository 6 | """ 7 | 8 | import chimere2kpp 9 | import geoschem2kpp 10 | import cmaq2kpp 11 | import sbox2kpp -------------------------------------------------------------------------------- /pykpp/models/prep/am32kpp.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import sys 3 | import re 4 | intxt = file(sys.argv[1], 'r').read() 5 | 6 | counter = 0 7 | commentonly = re.compile('(=\s*{[^}]+}\s*):') 8 | intertspcs = r'(?:\b(?:M|H2|CO2|H2O|hv)\b)' 9 | stoic = r'(?:\d*\.?\d+\*)' 10 | first = '(?:(?<=)\s*' + stoic + '?' + intertspcs + '\s*\+?)' 11 | second = r'(?:\+[ ]*' + stoic + '?' + intertspcs + ')' 12 | inert = re.compile('((?:(?:' + first + '|' + second + ')\s*)+)', flags = re.M) 13 | def makedble(ra_in): 14 | ra_in = ra_in.strip() 15 | if 'd' in ra_in or 'D' in ra_in or ra_in == '': 16 | ra_out = ra_in 17 | elif 'e' in ra_in: 18 | ra_out = ra_in.replace('e', 'd') 19 | elif 'E' in ra_in: 20 | ra_out = ra_in.replace('E', 'D') 21 | elif '.' in ra_in: 22 | ra_out = ra_in + 'd0' 23 | else: 24 | ra_out = ra_in + '.d0' 25 | return ra_out 26 | 27 | def rateform(matcho): 28 | global counter 29 | gd = matcho.groupdict() 30 | kwds = {} 31 | kwds.update(gd) 32 | if kwds['more'] is None: 33 | kwds['more'] = '' 34 | if kwds['rateargs'] is None: 35 | kwds['rateargs'] = '' 36 | rateargs = kwds['rateargs'].strip() 37 | rateargs = [makedble(ra) for ra in rateargs.split(',')] 38 | kwds['rateargs'] = rateargs 39 | label = gd['label'] 40 | ratefunc = kwds['ratefunc'] = 'am3_' + {None: 'standard'}.get(label, label) 41 | nargs = len(rateargs) 42 | minarg = dict(am3_standard = 3).get(ratefunc, nargs) 43 | rateargs = rateargs + ['0.0d0'] * (minarg - nargs) 44 | kwds['rateargs'] = ', '.join(rateargs) 45 | counter += 1 46 | kwds['counter'] = counter 47 | reaction = (kwds['reaction'] + kwds['more']).replace('\t', ' ') 48 | reactants, products = reaction.split('->') 49 | reactants = inert.sub(r'{\1}', reactants) 50 | products = inert.sub(r'{\1}', products) 51 | reaction = reactants + '->' + products 52 | kwds['factor'] = '' 53 | kwds['reaction'] = reaction 54 | 55 | out = '%(reaction)s : %(factor)s%(ratefunc)s(%(rateargs)s);\n' % kwds 56 | out = out.replace('*', ' ').replace(' ', ' ').replace(' ', ' ').replace(' ', ' ').replace(' ', ' ').replace(' ', ' ').replace(' ', ' ').replace(' ', ' ').replace(' ', ' ').replace(' ', ' ').replace(' ', ' ').replace(' ', ' ') 57 | if kwds['comment'] == '*': 58 | out = '{active?}' + out 59 | return '\n' + out 60 | 61 | 62 | species_g = re.search(r'^\s*Explicit\s*?\n(?P.+)^\s*End Explicit\s*Implicit\s*\n(?P.+)^\s*End Implicit', intxt, re.M + re.DOTALL).groupdict() 63 | species_txt = '#DEFVAR\n' + ' = IGNORE;\n'.join((species_g['explicit'] + '\n'+ species_g['implicit']).strip().replace('\n', ',').replace(' ', '').replace(',,', ',').replace(',,', ',').replace(',,', ',').split(',')) + ' = IGNORE;\n' 64 | photolysis_txt = re.search(r'^\s*Photolysis\s*\n(?P.+)^\s*End Photolysis', intxt, re.M + re.DOTALL).groupdict()['photolysis'] 65 | kinetic_txt = re.search(r'^\s*Reactions\s*\n(?P.+)^\s*End Reactions', intxt, re.M + re.DOTALL).groupdict()['kinetic'] 66 | 67 | n = -1 68 | while n != 0: 69 | photolysis_txt, n = re.subn(r'(?:^|\n)\s*(?:\[(\S+)\])?\s*([0-9a-zA-Z* >+-.\t]+)', r'\n{\1}: \2: TUV_J(\1, THETA, 1);', photolysis_txt, flags = re.M) 70 | photolysis_txt = inert.sub(r'{\1}', photolysis_txt) 71 | 72 | n = -1 73 | while n != 0: 74 | kinetic_txt, n = re.subn(r'(?:^|\n)(?P\*)?\s*(?:\[(?P