pymrm 2.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pymrm-2.0.0/.gitignore +5 -0
- pymrm-2.0.0/.vscode/settings.json +6 -0
- pymrm-2.0.0/MANIFEST.in +2 -0
- pymrm-2.0.0/Makefile +20 -0
- pymrm-2.0.0/PKG-INFO +45 -0
- pymrm-2.0.0/README.md +27 -0
- pymrm-2.0.0/conf.py +54 -0
- pymrm-2.0.0/conf.rst +7 -0
- pymrm-2.0.0/docs/dependencies_format.md +111 -0
- pymrm-2.0.0/examples/counter_current_reaction.ipynb +138 -0
- pymrm-2.0.0/examples/counter_diffusion_reaction.ipynb +112 -0
- pymrm-2.0.0/examples/darcy_flow.ipynb +129 -0
- pymrm-2.0.0/examples/lid_driven_cavity.ipynb +210 -0
- pymrm-2.0.0/examples/optimization_example.ipynb +94 -0
- pymrm-2.0.0/examples/particle_model_effectiveness.ipynb +116 -0
- pymrm-2.0.0/examples/pymrm_examples.ipynb +1029 -0
- pymrm-2.0.0/examples/tvd.ipynb +200 -0
- pymrm-2.0.0/examples/unsteady_convection_reaction.ipynb +122 -0
- pymrm-2.0.0/examples/unsteady_diffusion_reaction_2D.ipynb +179 -0
- pymrm-2.0.0/exercises/L1/Exercise 1.1 - First order reaction in a batch reactor.ipynb +279 -0
- pymrm-2.0.0/exercises/L1/Exercise 1.2 - Equilibrium-consecutive reaction system in a batch reactor.ipynb +396 -0
- pymrm-2.0.0/exercises/L1/Exercise 1.3 - Steady-state 1D adiabatic fixed bed reactor model.ipynb +174 -0
- pymrm-2.0.0/exercises/L2/Exercise 2.1 - Single component convection.ipynb +179 -0
- pymrm-2.0.0/exercises/L2/Exercise 2.1 - Single component convection.py +112 -0
- pymrm-2.0.0/exercises/L3/Exercise 3.1 - one component 1D diffusion.ipynb +112 -0
- pymrm-2.0.0/exercises/L3/Exercise 3.2 - Multicomponent Diffusion.ipynb +119 -0
- pymrm-2.0.0/exercises/L3/Exercise 3.2 - counter diffusion.ipynb +189 -0
- pymrm-2.0.0/exercises/L3/Exercise 3.3 - Multi-component 1D counter-current convection with reaction.ipynb +156 -0
- pymrm-2.0.0/exercises/L3/Exercise 3.4 - Convection Diffusion.ipynb +121 -0
- pymrm-2.0.0/exercises/L3/Exercise 3.5 - Particle model.ipynb +135 -0
- pymrm-2.0.0/exercises/L3/Exercise 3.6 - WeiszHicks.ipynb +221 -0
- pymrm-2.0.0/exercises/L4/Exercise 4.1 - Counter-current column processes.ipynb +110 -0
- pymrm-2.0.0/exercises/L4/Exercise 4.2 - Reactor model for heterogeneous bubble columns.ipynb +172 -0
- pymrm-2.0.0/exercises/L7/Exercise 7.2.ipynb +182 -0
- pymrm-2.0.0/exercises/L7/Exercise 7.2.py +133 -0
- pymrm-2.0.0/make.bat +35 -0
- pymrm-2.0.0/modules.rst +6 -0
- pymrm-2.0.0/mrm_environment.yml +15 -0
- pymrm-2.0.0/mrm_python_install.md +49 -0
- pymrm-2.0.0/mrm_requirements.txt +8 -0
- pymrm-2.0.0/pymrm/__init__.py +21 -0
- pymrm-2.0.0/pymrm/numjac.py +472 -0
- pymrm-2.0.0/pymrm/pymrm.egg-info/PKG-INFO +45 -0
- pymrm-2.0.0/pymrm/pymrm.egg-info/SOURCES.txt +49 -0
- pymrm-2.0.0/pymrm/pymrm.egg-info/dependency_links.txt +1 -0
- pymrm-2.0.0/pymrm/pymrm.egg-info/requires.txt +3 -0
- pymrm-2.0.0/pymrm/pymrm.egg-info/top_level.txt +1 -0
- pymrm-2.0.0/pymrm/pymrm_core.py +1168 -0
- pymrm-2.0.0/pyproject.toml +40 -0
- pymrm-2.0.0/setup.cfg +4 -0
- pymrm-2.0.0/test_mrm.rst +7 -0
pymrm-2.0.0/.gitignore
ADDED
pymrm-2.0.0/MANIFEST.in
ADDED
pymrm-2.0.0/Makefile
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
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 = .
|
|
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
|
+
# Catch-all target: route all unknown targets to Sphinx using the new
|
|
18
|
+
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
|
19
|
+
%: Makefile
|
|
20
|
+
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
pymrm-2.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: pymrm
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: Functions for multiphase reactor modeling
|
|
5
|
+
Author-email: "E.A.J.F. Peters" <e.a.j.f.peters@tue.nl>, "M. van Sint Annaland" <M.v.SintAnnaland@tue.nl>, "M. Galanti" <m.galanti@tue.nl>, "D.R. Rieder" <d.r.rieder@tue.nl>
|
|
6
|
+
Maintainer-email: "E.A.J.F. Peters" <e.a.j.f.peters@tue.nl>
|
|
7
|
+
Project-URL: Homepage, https://gitlab.tue.nl/SMM/research_projects/eajfpeters/pymrm/
|
|
8
|
+
Project-URL: Repository, https://gitlab.tue.nl/SMM/research_projects/eajfpeters/pymrm/
|
|
9
|
+
Keywords: MRM,modeling,multiphase,diffusion,convection
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Programming Language :: Python
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Requires-Python: >=3.10
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
Requires-Dist: numpy>=1.26
|
|
16
|
+
Requires-Dist: scipy>=1.13
|
|
17
|
+
Requires-Dist: numba>=0.58
|
|
18
|
+
|
|
19
|
+
# pymrm: Python Package for Multiphase Reactor Modeling
|
|
20
|
+
|
|
21
|
+
## Overview
|
|
22
|
+
|
|
23
|
+
Welcome to pymrm, a Python package designed for creating phenomenological models of multiphase reactors. Developed within the ambit of the Multiphase Reactor Modeling course at Eindhoven University of Technology, this package finds its roots in the Department of Chemical Engineering and Chemistry taught by Martin van Sint Annaland and Frank Peters. Originally implemented in Matlab, pymrm stands today as a port to Python, signifying a step forward in accessibility and open access.
|
|
24
|
+
|
|
25
|
+
## Getting Started
|
|
26
|
+
|
|
27
|
+
To get a comprehensive understanding of pymrm's capabilities, we recommend exploring the included Jupyter notebook. This notebook is packed with examples that demonstrate the application of the package in the realm of chemical engineering.
|
|
28
|
+
|
|
29
|
+
### Features
|
|
30
|
+
|
|
31
|
+
- Comprehensive set of functions for modeling multiphase reactors
|
|
32
|
+
- User-friendly interface tailored for both students and professionals
|
|
33
|
+
- Extensive documentation and example-based learning
|
|
34
|
+
|
|
35
|
+
### How to Install
|
|
36
|
+
|
|
37
|
+
pip install pymrm
|
|
38
|
+
|
|
39
|
+
### Quickstart Guide
|
|
40
|
+
|
|
41
|
+
Start implementing 1D reaction-diffusion equations, proceed to convection, different phases, mass transfer, etc. The Python notebook: pymrm_examples.ipynb provides examples that are useful to get you started.
|
|
42
|
+
|
|
43
|
+
### Contributing
|
|
44
|
+
|
|
45
|
+
We welcome contributions from the community. If you're interested in enhancing pymrm or have suggestions, please contact us.
|
pymrm-2.0.0/README.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# pymrm: Python Package for Multiphase Reactor Modeling
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Welcome to pymrm, a Python package designed for creating phenomenological models of multiphase reactors. Developed within the ambit of the Multiphase Reactor Modeling course at Eindhoven University of Technology, this package finds its roots in the Department of Chemical Engineering and Chemistry taught by Martin van Sint Annaland and Frank Peters. Originally implemented in Matlab, pymrm stands today as a port to Python, signifying a step forward in accessibility and open access.
|
|
6
|
+
|
|
7
|
+
## Getting Started
|
|
8
|
+
|
|
9
|
+
To get a comprehensive understanding of pymrm's capabilities, we recommend exploring the included Jupyter notebook. This notebook is packed with examples that demonstrate the application of the package in the realm of chemical engineering.
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
- Comprehensive set of functions for modeling multiphase reactors
|
|
14
|
+
- User-friendly interface tailored for both students and professionals
|
|
15
|
+
- Extensive documentation and example-based learning
|
|
16
|
+
|
|
17
|
+
### How to Install
|
|
18
|
+
|
|
19
|
+
pip install pymrm
|
|
20
|
+
|
|
21
|
+
### Quickstart Guide
|
|
22
|
+
|
|
23
|
+
Start implementing 1D reaction-diffusion equations, proceed to convection, different phases, mass transfer, etc. The Python notebook: pymrm_examples.ipynb provides examples that are useful to get you started.
|
|
24
|
+
|
|
25
|
+
### Contributing
|
|
26
|
+
|
|
27
|
+
We welcome contributions from the community. If you're interested in enhancing pymrm or have suggestions, please contact us.
|
pymrm-2.0.0/conf.py
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
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
|
+
sys.path.insert(0, os.path.abspath('.'))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# -- Project information -----------------------------------------------------
|
|
19
|
+
|
|
20
|
+
project = 'pymrm'
|
|
21
|
+
copyright = '2024, E.A.J.F. Peters'
|
|
22
|
+
author = 'E.A.J.F. Peters'
|
|
23
|
+
|
|
24
|
+
# The full version, including alpha/beta/rc tags
|
|
25
|
+
release = '2.0.0'
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# -- General configuration ---------------------------------------------------
|
|
29
|
+
|
|
30
|
+
# Add any Sphinx extension module names here, as strings. They can be
|
|
31
|
+
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
|
32
|
+
# ones.
|
|
33
|
+
extensions = ['sphinx.ext.autodoc']
|
|
34
|
+
|
|
35
|
+
# Add any paths that contain templates here, relative to this directory.
|
|
36
|
+
templates_path = ['_templates']
|
|
37
|
+
|
|
38
|
+
# List of patterns, relative to source directory, that match files and
|
|
39
|
+
# directories to ignore when looking for source files.
|
|
40
|
+
# This pattern also affects html_static_path and html_extra_path.
|
|
41
|
+
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# -- Options for HTML output -------------------------------------------------
|
|
45
|
+
|
|
46
|
+
# The theme to use for HTML and HTML Help pages. See the documentation for
|
|
47
|
+
# a list of builtin themes.
|
|
48
|
+
#
|
|
49
|
+
html_theme = 'sphinx_rtd_theme'
|
|
50
|
+
|
|
51
|
+
# Add any paths that contain custom static files (such as style sheets) here,
|
|
52
|
+
# relative to this directory. They are copied after the builtin static files,
|
|
53
|
+
# so a file named "default.css" will overwrite the builtin "default.css".
|
|
54
|
+
html_static_path = ['_static']
|
pymrm-2.0.0/conf.rst
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# PyMRM Dependency Notation
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The **PyMRM Dependency Notation** provides a structured and flexible way to describe dependencies in operations involving discretized fields, such as those used in the **PyMRM simulation package**. These fields are represented as multidimensional arrays, which could model, for example, concentrations of multiple species in a 2D or 3D system.
|
|
6
|
+
|
|
7
|
+
### Key Concepts
|
|
8
|
+
- A **field function** maps an input field to an output field of the same shape, such as a Laplace operator or a reaction rate calculation.
|
|
9
|
+
- A **dependency notation** specifies how values at one position in the field depend on values at other positions.
|
|
10
|
+
- Dependencies are particularly relevant for:
|
|
11
|
+
- **Spatial axes**: Often involve nearest-neighbor interactions (e.g., stencils).
|
|
12
|
+
- **Non-spatial axes**: Often involve absolute dependencies between indices (e.g., reaction rates between species).
|
|
13
|
+
|
|
14
|
+
This notation is primarily used internally in PyMRM to construct efficient sparsity patterns for Jacobians. Advanced users can also leverage it to define custom field operations.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Dependency Notation
|
|
19
|
+
|
|
20
|
+
A single dependency is represented as a **tuple of four elements**:
|
|
21
|
+
|
|
22
|
+
```text
|
|
23
|
+
(function_index, field_index, fixed_axes_list, periodic_axes_list)
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Components
|
|
27
|
+
|
|
28
|
+
1. **`function_index`**:
|
|
29
|
+
- A tuple representing the position in the output (function) array.
|
|
30
|
+
- Only relevant for axes listed in the `fixed_axes_list`.
|
|
31
|
+
- Can be set to `None` if all axes are spatial (no fixed axes).
|
|
32
|
+
|
|
33
|
+
2. **`field_index`**:
|
|
34
|
+
- A tuple representing the position in the input (field) array that contributes to the value at `function_index`.
|
|
35
|
+
|
|
36
|
+
3. **`fixed_axes_list`**:
|
|
37
|
+
- A list of axes that are considered "fixed" (non-spatial), such as axes representing species or phases.
|
|
38
|
+
- These axes require an explicit reference in `function_index`.
|
|
39
|
+
|
|
40
|
+
4. **`periodic_axes_list`**:
|
|
41
|
+
- A list of spatial axes that are considered periodic, where values near one boundary depend on values near the opposite boundary.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
### Key Rules
|
|
46
|
+
- **Spatial axes**: Dependencies on spatial axes often follow stencils (e.g., nearest neighbors).
|
|
47
|
+
- **Fixed axes**: The `function_index` specifies the fixed position on these axes.
|
|
48
|
+
- **Ignored values**: For axes not listed in the `fixed_axes_list`, the values in `function_index` are ignored. If `fixed_axes_list` is empty, `function_index` can be `None`.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
### Examples
|
|
53
|
+
|
|
54
|
+
#### Simple Stencils (Spatial Dependencies Only)
|
|
55
|
+
1. `(None, (0, 0, 0), [], [])`: The value at `(i, j, k)` depends on the field value at `(i, j, k)`.
|
|
56
|
+
2. `(None, (0, 1, 0), [], [])`: The value at `(i, j, k)` depends on the field value at `(i, j+1, k)`.
|
|
57
|
+
3. `(None, (-1, 1, 0), [], [])`: The value at `(i, j, k)` depends on the field value at `(i-1, j+1, k)`.
|
|
58
|
+
|
|
59
|
+
#### Fixed Axes (Non-Spatial Dependencies)
|
|
60
|
+
1. `((0, 0, 0), (-1, 1, 0), [2], [])`: The value at `(i, j, 0)` depends on the field value at `(i-1, j+1, 0)`.
|
|
61
|
+
2. `((0, 0, 0), (-1, 1, 1), [2], [])`: The value at `(i, j, 0)` depends on the field value at `(i-1, j+1, 1)`.
|
|
62
|
+
3. `((0, 0, 1), (-1, 1, 0), [2], [])`: The value at `(i, j, 1)` depends on the field value at `(i-1, j+1, 0)`.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Shorthand Notation
|
|
67
|
+
|
|
68
|
+
To simplify commonly used patterns, the dependency notation supports the following shorthand conventions:
|
|
69
|
+
|
|
70
|
+
### 1. **Simple Stencils**
|
|
71
|
+
For stencils with no `fixed_axes_list` or `periodic_axes_list`, the structure can be reduced to a single tuple:
|
|
72
|
+
|
|
73
|
+
- `(None, (0, 0, 0), [], [])` → `(0, 0, 0)`
|
|
74
|
+
- `(None, (0, 1, 0), [], [])` → `(0, 1, 0)`
|
|
75
|
+
- `(None, (-1, 1, 0), [], [])` → `(-1, 1, 0)`
|
|
76
|
+
|
|
77
|
+
### 2. **No Periodic Axes**
|
|
78
|
+
If `periodic_axes_list` is empty, it can be omitted:
|
|
79
|
+
|
|
80
|
+
- `((0, 0, 0), (0, 0, 1), [2], [])` → `((0, 0, 0), (0, 0, 1), [2])`
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## List of Dependencies
|
|
85
|
+
|
|
86
|
+
A full sparsity pattern is described as a **list of single dependencies**. For example:
|
|
87
|
+
|
|
88
|
+
1. **1D Stencil** (Three-Point Stencil):
|
|
89
|
+
- `[(-1, 0), (0, 0), (1, 0)]`
|
|
90
|
+
|
|
91
|
+
2. **3D Stencil** (Seven-Point Stencil):
|
|
92
|
+
- `[(0, 0, 0), (-1, 0, 0), (1, 0, 0), (0, -1, 0), (0, 1, 0), (0, 0, -1), (0, 0, 1)]`
|
|
93
|
+
|
|
94
|
+
### Compact Notation for Ranges
|
|
95
|
+
Ranges and lists can be expressed using slices or nested lists for convenience:
|
|
96
|
+
|
|
97
|
+
- `[(-1, 0), (0, 0), (1, 0)]` → `[([-1, 0, 1], 0)]`
|
|
98
|
+
- Seven-Point Stencil:
|
|
99
|
+
- `[(0, 0, 0), (-1, 0, 0), ..., (0, 0, 1)]` → `[([-1, 0, 1], 0, 0), (0, [-1, 0, 1], 0), (0, 0, [-1, 0, 1])]`
|
|
100
|
+
|
|
101
|
+
### Example: Multi-Component Diffusion
|
|
102
|
+
In a multi-component diffusion problem, the function value for one species depends on all species in neighboring cells:
|
|
103
|
+
```python
|
|
104
|
+
dependencies = ((0, slice(None)), ([-1, 0, 1], slice(None)), [1])
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Conclusion
|
|
110
|
+
|
|
111
|
+
The **PyMRM Dependency Notation** provides a consistent and flexible way to describe dependencies in discretized field operations. While it is primarily used internally, it offers advanced users the ability to define custom operations with precise control over sparsity patterns. This capability is crucial for optimizing Jacobian calculations and other computationally intensive tasks.
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cells": [
|
|
3
|
+
{
|
|
4
|
+
"cell_type": "markdown",
|
|
5
|
+
"id": "7b8bd294",
|
|
6
|
+
"metadata": {},
|
|
7
|
+
"source": [
|
|
8
|
+
"# Counter-current convection and reaction\n",
|
|
9
|
+
"\n",
|
|
10
|
+
"Reaction: $A+B\\rightarrow *$\n",
|
|
11
|
+
"\n",
|
|
12
|
+
"$\\frac{\\partial c_A}{\\partial t} + \\mathrm{div}(v_A \\, c_A) = -k \\, c_A c_B$\n",
|
|
13
|
+
"\n",
|
|
14
|
+
"$\\frac{\\partial c_B}{\\partial t} + \\mathrm{div}(v_B \\, c_B) = -k \\, c_A c_B$\n",
|
|
15
|
+
"\n",
|
|
16
|
+
"with boundary conditions: $c_A(0)=1$, $\\frac{\\partial c_A(L)}{\\partial n} = 0$, $\\frac{\\partial c_B(0)}{\\partial n} = 0$, $c_B(L)=1$\n",
|
|
17
|
+
"\n",
|
|
18
|
+
"Notes: \n",
|
|
19
|
+
"- Counter-current flow is implemented by taking $v_B$ negative and setting the proper boundary conditions.\n",
|
|
20
|
+
"- With $|v_A| > |v_B|$ and same inlet concentrations not all $A$ will react away.\n",
|
|
21
|
+
"- For high reaction rate coefficient $k$, there is only a thin boundary layer where $B$ is present. If this boundary layer is not resolved the result is incorrect. Uncomment the non-uniform mesh for high $k$.\n",
|
|
22
|
+
"- This is a numerically tough problem for high reaction rates. Note that TVD deferred correction is currently off, i.e. the upwind scheme is used. TVD seems to decrease robustness."
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"cell_type": "code",
|
|
27
|
+
"execution_count": 7,
|
|
28
|
+
"id": "c3456619",
|
|
29
|
+
"metadata": {},
|
|
30
|
+
"outputs": [
|
|
31
|
+
{
|
|
32
|
+
"data": {
|
|
33
|
+
"text/plain": [
|
|
34
|
+
"'progress: 100 out of 100'"
|
|
35
|
+
]
|
|
36
|
+
},
|
|
37
|
+
"metadata": {},
|
|
38
|
+
"output_type": "display_data"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"data": {
|
|
42
|
+
"image/png": "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mjhxoq677jp9//33wXwFAADQGLzDUmZvKL755psb5cMXL16syZMn6+6775YkLVmyRBs3btSyZcuUmZl53uPuvfdeTZgwQXa7XW+99VajxAIAAILgnS1l9srNggULGvzBFRUV2rVrl+bMmeO3PSMjQzt27DjvcatWrdL+/fv117/+VY899tgFP8fpdMrpdPr+LikpCT5oAADgzzcsFT49Nw1axG/Xrl3KycmRzWZTv379NGjQoHofW1hYKJfLpcTERL/tiYmJys/Pr/WYffv2ac6cOdq6dascjvqFnpmZqYULF9Y7LgAAEIAwnC0VVHJTUFCgX/3qV/rwww/Vtm1bGYah4uJijRw5UmvXrlWnTp3qfa5z18gxDKPWdXNcLpcmTJighQsXqnfv3vU+/9y5czVz5kzf3yUlJUpOTq738QAAoA5V3uQmfIalgpot9eCDD6qkpERfffWVjh8/rhMnTujLL79USUmJpk6dWq9zdOzYUXa7vUaVpqCgoEY1R5JKS0u1c+dOPfDAA3I4HHI4HFq0aJG++OILORwOvf/++7V+TnR0tOLj4/0eAACgkfgqNyYflnr33Xf197//XX379vVt69evn55//nllZGTU6xxRUVFKS0tTdna2fvGLX/i2Z2dna9y4cTX2j4+P1z//+U+/bUuXLtX777+v119/XampqcF8FQAA0BBWmS3ldrtrTP+WpMjIyBr3marLzJkzdfvttys9PV1Dhw7Viy++qNzcXE2ZMkWSZ0jp6NGj+vOf/6yIiAgNGDDA7/jOnTsrJiamxnYAANBMrLLOzbXXXqtp06bp1VdfVdeuXSVJR48e1YwZM3TdddfV+zzjx49XUVGRFi1apLy8PA0YMEBZWVlKSUmRJOXl5Sk3NzeYEAEAQHNwhd8KxTbDMIxADzp8+LDGjRunL7/8UsnJybLZbMrNzdVll12m//3f/1X37t2bItZGUVJSooSEBBUXF9N/AwBAQ219Wtq0SBr0H9K455vsYwL5/Q6qcpOcnKzPP/9c2dnZ+te//iXDMNSvXz9df/31QQUMAABMyjcsFT49NwHNlnr//ffVr18/30J4o0aN0oMPPqipU6fqyiuvVP/+/et9bygAAGABZr9x5pIlS3TPPffUWg5KSEjQvffeq8WLFzdacAAAIMy5TL7OzRdffKEbbrjhvO9nZGRo165dDQ4KAACY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",
|
|
43
|
+
"text/plain": [
|
|
44
|
+
"<Figure size 640x480 with 1 Axes>"
|
|
45
|
+
]
|
|
46
|
+
},
|
|
47
|
+
"metadata": {},
|
|
48
|
+
"output_type": "display_data"
|
|
49
|
+
}
|
|
50
|
+
],
|
|
51
|
+
"source": [
|
|
52
|
+
"import numpy as np\n",
|
|
53
|
+
"import scipy as sp\n",
|
|
54
|
+
"from scipy.sparse import linalg as sla\n",
|
|
55
|
+
"import matplotlib.pyplot as plt\n",
|
|
56
|
+
"from IPython.display import clear_output, display\n",
|
|
57
|
+
"from pymrm import non_uniform_grid, construct_convflux_upwind, construct_div, NumJac, interp_cntr_to_stagg_tvd, upwind, minmod\n",
|
|
58
|
+
"\n",
|
|
59
|
+
"# physical parameters and boundary conditions\n",
|
|
60
|
+
"num_c = 2\n",
|
|
61
|
+
"L = 1.0\n",
|
|
62
|
+
"k = 100\n",
|
|
63
|
+
"v = [[1,-0.5]]\n",
|
|
64
|
+
"bc_L = {'a': [[0,1]], 'b': [[1,0]], 'd': [[1, 0]]}\n",
|
|
65
|
+
"bc_R = {'a': [[1,0]], 'b': [[0,1]], 'd': [[0, 1]]}\n",
|
|
66
|
+
"\n",
|
|
67
|
+
"# Reaction function\n",
|
|
68
|
+
"def reaction(c, k):\n",
|
|
69
|
+
" f = np.empty_like(c)\n",
|
|
70
|
+
" r = k * c[:, 0] * c[:, 1]\n",
|
|
71
|
+
" f[:, 0] = -r\n",
|
|
72
|
+
" f[:, 1] = -r\n",
|
|
73
|
+
" return f\n",
|
|
74
|
+
"\n",
|
|
75
|
+
"# numerical parameters\n",
|
|
76
|
+
"dt = 1\n",
|
|
77
|
+
"num_time_steps = 100\n",
|
|
78
|
+
"num_inner_iter = 2\n",
|
|
79
|
+
"num_x = 100\n",
|
|
80
|
+
"\n",
|
|
81
|
+
"bc = (bc_L, bc_R)\n",
|
|
82
|
+
"shape = (num_x, num_c)\n",
|
|
83
|
+
"#x_f = np.linspace(0, L, num_x+1)\n",
|
|
84
|
+
"x_f = non_uniform_grid(0, L, shape[0]+1, 0.05*L, 0.75)\n",
|
|
85
|
+
"x_c = 0.5*(x_f[:-1] + x_f[1:])\n",
|
|
86
|
+
"\n",
|
|
87
|
+
"Conv, conv_bc = construct_convflux_upwind(shape, x_f, x_c, bc, v, axis=0)\n",
|
|
88
|
+
"Div = construct_div(shape, x_f, nu=0, axis=0)\n",
|
|
89
|
+
"g_const = (Div @ conv_bc)\n",
|
|
90
|
+
"Jac_const = sp.sparse.eye(np.prod(shape), format='csc')/dt + Div @ Conv\n",
|
|
91
|
+
"numjac_react = NumJac(shape, axes_blocks=[-1])\n",
|
|
92
|
+
"\n",
|
|
93
|
+
"c = np.zeros(shape)\n",
|
|
94
|
+
"for i in range(num_time_steps):\n",
|
|
95
|
+
" c_old = c\n",
|
|
96
|
+
" for j in range(num_inner_iter):\n",
|
|
97
|
+
" f_react, Jac_react = numjac_react(lambda c: reaction(c, k), c)\n",
|
|
98
|
+
" Jac = Jac_const - Jac_react\n",
|
|
99
|
+
" Jac_lu = sla.splu(Jac)\n",
|
|
100
|
+
" c_f, dc_f = interp_cntr_to_stagg_tvd(c, x_f, x_c, bc, v, upwind, axis=0)\n",
|
|
101
|
+
"# c_f, dc_f = interp_cntr_to_stagg_tvd(c, x_f, x_c, bc, v, minmod, axis=0)\n",
|
|
102
|
+
" g_conv_deferred = Div @ (v*dc_f).reshape((-1,1))\n",
|
|
103
|
+
" g = -c_old.reshape(-1,1)/dt + Jac_const @ c.reshape(-1,1) + g_conv_deferred + g_const -f_react.reshape(-1,1)\n",
|
|
104
|
+
" c -= Jac_lu.solve(g).reshape(c.shape)\n",
|
|
105
|
+
" clear_output(wait=True)\n",
|
|
106
|
+
" display(f'progress: {i+1} out of {num_time_steps}')\n",
|
|
107
|
+
" \n",
|
|
108
|
+
"fig, ax = plt.subplots()\n",
|
|
109
|
+
"line0, = ax.plot(x_c, c[:, 0])\n",
|
|
110
|
+
"line1, = ax.plot(x_c, c[:, 1])\n",
|
|
111
|
+
"ax.set_xlabel('Position')\n",
|
|
112
|
+
"ax.set_ylabel('Concentration')\n",
|
|
113
|
+
"fig.canvas.draw()\n"
|
|
114
|
+
]
|
|
115
|
+
}
|
|
116
|
+
],
|
|
117
|
+
"metadata": {
|
|
118
|
+
"kernelspec": {
|
|
119
|
+
"display_name": "mrm",
|
|
120
|
+
"language": "python",
|
|
121
|
+
"name": "python3"
|
|
122
|
+
},
|
|
123
|
+
"language_info": {
|
|
124
|
+
"codemirror_mode": {
|
|
125
|
+
"name": "ipython",
|
|
126
|
+
"version": 3
|
|
127
|
+
},
|
|
128
|
+
"file_extension": ".py",
|
|
129
|
+
"mimetype": "text/x-python",
|
|
130
|
+
"name": "python",
|
|
131
|
+
"nbconvert_exporter": "python",
|
|
132
|
+
"pygments_lexer": "ipython3",
|
|
133
|
+
"version": "3.12.3"
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
"nbformat": 4,
|
|
137
|
+
"nbformat_minor": 5
|
|
138
|
+
}
|