layercake-model 1.0.2a0__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.
- layercake_model-1.0.2a0/LICENSE.txt +11 -0
- layercake_model-1.0.2a0/PKG-INFO +142 -0
- layercake_model-1.0.2a0/README.md +109 -0
- layercake_model-1.0.2a0/layercake/__init__.py +16 -0
- layercake_model-1.0.2a0/layercake/arithmetic/equation.py +410 -0
- layercake_model-1.0.2a0/layercake/arithmetic/symbolic/__init__.py +0 -0
- layercake_model-1.0.2a0/layercake/arithmetic/symbolic/expressions.py +73 -0
- layercake_model-1.0.2a0/layercake/arithmetic/symbolic/operators.py +338 -0
- layercake_model-1.0.2a0/layercake/arithmetic/terms/__init__.py +9 -0
- layercake_model-1.0.2a0/layercake/arithmetic/terms/base.py +884 -0
- layercake_model-1.0.2a0/layercake/arithmetic/terms/constant.py +119 -0
- layercake_model-1.0.2a0/layercake/arithmetic/terms/gradient.py +185 -0
- layercake_model-1.0.2a0/layercake/arithmetic/terms/jacobian.py +183 -0
- layercake_model-1.0.2a0/layercake/arithmetic/terms/linear.py +98 -0
- layercake_model-1.0.2a0/layercake/arithmetic/terms/operations.py +262 -0
- layercake_model-1.0.2a0/layercake/arithmetic/terms/operators.py +226 -0
- layercake_model-1.0.2a0/layercake/arithmetic/utils.py +20 -0
- layercake_model-1.0.2a0/layercake/bakery/__init__.py +0 -0
- layercake_model-1.0.2a0/layercake/bakery/cake.py +705 -0
- layercake_model-1.0.2a0/layercake/bakery/layers.py +569 -0
- layercake_model-1.0.2a0/layercake/basis/__init__.py +4 -0
- layercake_model-1.0.2a0/layercake/basis/base.py +244 -0
- layercake_model-1.0.2a0/layercake/basis/planar_fourier.py +418 -0
- layercake_model-1.0.2a0/layercake/basis/spherical_harmonics.py +166 -0
- layercake_model-1.0.2a0/layercake/formatters/__init__.py +0 -0
- layercake_model-1.0.2a0/layercake/formatters/base.py +183 -0
- layercake_model-1.0.2a0/layercake/formatters/fortran.py +85 -0
- layercake_model-1.0.2a0/layercake/formatters/julia.py +93 -0
- layercake_model-1.0.2a0/layercake/formatters/python.py +89 -0
- layercake_model-1.0.2a0/layercake/inner_products/__init__.py +0 -0
- layercake_model-1.0.2a0/layercake/inner_products/definition.py +218 -0
- layercake_model-1.0.2a0/layercake/utils/__init__.py +8 -0
- layercake_model-1.0.2a0/layercake/utils/commutativity.py +45 -0
- layercake_model-1.0.2a0/layercake/utils/integration.py +200 -0
- layercake_model-1.0.2a0/layercake/utils/matrix.py +104 -0
- layercake_model-1.0.2a0/layercake/utils/parallel.py +215 -0
- layercake_model-1.0.2a0/layercake/utils/symbolic_tensor.py +172 -0
- layercake_model-1.0.2a0/layercake/utils/tensor.py +83 -0
- layercake_model-1.0.2a0/layercake/variables/__init__.py +0 -0
- layercake_model-1.0.2a0/layercake/variables/coordinate.py +65 -0
- layercake_model-1.0.2a0/layercake/variables/field.py +535 -0
- layercake_model-1.0.2a0/layercake/variables/parameter.py +664 -0
- layercake_model-1.0.2a0/layercake/variables/systems.py +143 -0
- layercake_model-1.0.2a0/layercake/variables/utils.py +142 -0
- layercake_model-1.0.2a0/layercake/variables/variable.py +314 -0
- layercake_model-1.0.2a0/layercake_model.egg-info/PKG-INFO +142 -0
- layercake_model-1.0.2a0/layercake_model.egg-info/SOURCES.txt +55 -0
- layercake_model-1.0.2a0/layercake_model.egg-info/dependency_links.txt +1 -0
- layercake_model-1.0.2a0/layercake_model.egg-info/requires.txt +7 -0
- layercake_model-1.0.2a0/layercake_model.egg-info/top_level.txt +1 -0
- layercake_model-1.0.2a0/pyproject.toml +61 -0
- layercake_model-1.0.2a0/setup.cfg +4 -0
- layercake_model-1.0.2a0/test/test_RP1982_qgs_numerical.py +330 -0
- layercake_model-1.0.2a0/test/test_RP1982_qgs_symbolic.py +340 -0
- layercake_model-1.0.2a0/test/test_base.py +82 -0
- layercake_model-1.0.2a0/test/test_maooam_qgs_numerical.py +444 -0
- layercake_model-1.0.2a0/test/test_maooam_qgs_symbolic.py +455 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Copyright 2025-2026 Jonathan Demaeyer and Oisín Hamilton
|
|
2
|
+
|
|
3
|
+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
|
4
|
+
|
|
5
|
+
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
|
6
|
+
|
|
7
|
+
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
|
8
|
+
|
|
9
|
+
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
|
10
|
+
|
|
11
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: layercake_model
|
|
3
|
+
Version: 1.0.2a0
|
|
4
|
+
Summary: A framework to design systems of partial differential equations (PDEs), and convert them to ordinary differential equations (ODEs) via Galerkin-type expansions.
|
|
5
|
+
Author-email: Jonathan Demaeyer <jodemaey@meteo.be>, Oisín Hamilton <ush.hamilton@gmail.com>
|
|
6
|
+
License-Expression: BSD-3-Clause
|
|
7
|
+
Project-URL: homepage, https://github.com/Climdyn/LayerCake
|
|
8
|
+
Project-URL: documentation, https://climdyn.github.io/LayerCake/
|
|
9
|
+
Keywords: pde,symbolic-computation,ode-model
|
|
10
|
+
Classifier: Programming Language :: Python
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Development Status :: 4 - Beta
|
|
19
|
+
Classifier: Intended Audience :: Science/Research
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: Atmospheric Science
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE.txt
|
|
25
|
+
Requires-Dist: matplotlib
|
|
26
|
+
Requires-Dist: numpy
|
|
27
|
+
Requires-Dist: numba
|
|
28
|
+
Requires-Dist: scipy
|
|
29
|
+
Requires-Dist: sympy
|
|
30
|
+
Requires-Dist: sparse
|
|
31
|
+
Requires-Dist: pebble
|
|
32
|
+
Dynamic: license-file
|
|
33
|
+
|
|
34
|
+
# LayerCake
|
|
35
|
+
|
|
36
|
+
> “Welcome to the layer cake son.”
|
|
37
|
+
>
|
|
38
|
+
> -- *Michael Gambon - Layer Cake (2004)*
|
|
39
|
+
|
|
40
|
+
[](https://doi.org/10.5281/zenodo.19789922)
|
|
41
|
+
[](https://climdyn.github.io/LayerCake/)
|
|
42
|
+
|
|
43
|
+
## General Information
|
|
44
|
+
|
|
45
|
+
LayerCake is a framework to design models based on systems of partial differential equations (PDEs),
|
|
46
|
+
and convert them to ordinary differential equations (ODEs) via Galerkin-type expansions.
|
|
47
|
+
|
|
48
|
+
LayerCake allows you to construct systems of PDEs, and to specify coordinate systems and basis functions to build
|
|
49
|
+
the corresponding ODE systems.
|
|
50
|
+
To build these systems, LayerCake relies heavily on the [Sympy](https://www.sympy.org/) symbolic computation framework.
|
|
51
|
+
The output of this procedure is either a [Numbaified](https://numba.pydata.org/) Python callable,
|
|
52
|
+
or a list of symbolic ODE tendencies that can be integrated in any of the supported languages
|
|
53
|
+
(Fortran, Julia and Python for the moment). These two kinds of output allow for the study of the computed models with
|
|
54
|
+
the modern tools available in all these languages.
|
|
55
|
+
|
|
56
|
+
LayerCake has been designed with geophysics in mind, although it may be useful for other applications.
|
|
57
|
+
|
|
58
|
+
## About
|
|
59
|
+
|
|
60
|
+
(c) 2025-2026 Jonathan Demaeyer and Oisín Hamilton
|
|
61
|
+
|
|
62
|
+
See [LICENSE.txt](https://github.com/Climdyn/LayerCake/blob/main/LICENSE.txt) for license information.
|
|
63
|
+
|
|
64
|
+
## Installation
|
|
65
|
+
|
|
66
|
+
### With pip
|
|
67
|
+
|
|
68
|
+
The easiest way to install and run LayerCake is to use [pip](https://pypi.org/).
|
|
69
|
+
Type in a terminal
|
|
70
|
+
|
|
71
|
+
pip install layercake-model
|
|
72
|
+
|
|
73
|
+
and you are set!
|
|
74
|
+
|
|
75
|
+
Additionally, you can clone the repository
|
|
76
|
+
|
|
77
|
+
git clone https://github.com/Climdyn/LayerCake.git
|
|
78
|
+
|
|
79
|
+
and perform a test by running the script
|
|
80
|
+
|
|
81
|
+
python examples/atmospheric/barotropic_one_layer.py
|
|
82
|
+
|
|
83
|
+
to see if everything runs smoothly (this should take less than 5 minutes).
|
|
84
|
+
|
|
85
|
+
### With Anaconda
|
|
86
|
+
|
|
87
|
+
The second-easiest way to install and run LayerCake is to use an appropriate environment
|
|
88
|
+
created through [Anaconda](https://www.anaconda.com/).
|
|
89
|
+
|
|
90
|
+
First install Anaconda and clone the repository:
|
|
91
|
+
|
|
92
|
+
git clone https://github.com/jodemaey/LayerCake.git
|
|
93
|
+
|
|
94
|
+
Then install and activate the Python3 Anaconda environment:
|
|
95
|
+
|
|
96
|
+
conda env create -f environment.yml
|
|
97
|
+
conda activate layercake
|
|
98
|
+
|
|
99
|
+
You can then perform a test by running the script
|
|
100
|
+
|
|
101
|
+
python examples/atmospheric/barotropic_one_layer.py
|
|
102
|
+
|
|
103
|
+
to see if everything runs smoothly (this should take less than 5 minutes to run).
|
|
104
|
+
|
|
105
|
+
## Documentation
|
|
106
|
+
|
|
107
|
+
To build the documentation, please run (with the conda environment activated):
|
|
108
|
+
|
|
109
|
+
cd documentation
|
|
110
|
+
make html
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
You may need to install [make](https://www.gnu.org/software/make/) if it is not already present on your system.
|
|
114
|
+
Once built, the documentation is available [here](./documentation/build/html/index.html).
|
|
115
|
+
|
|
116
|
+
The documentation is also available online at https://climdyn.github.io/LayerCake/. In particular,
|
|
117
|
+
please consider reading the [User guide](https://climdyn.github.io/LayerCake/files/user_guide.html#).
|
|
118
|
+
|
|
119
|
+
## Examples
|
|
120
|
+
|
|
121
|
+
A few examples are available in the [examples](./examples) folder. More examples will be provided as the code is
|
|
122
|
+
further developed.
|
|
123
|
+
|
|
124
|
+
## Dependencies
|
|
125
|
+
|
|
126
|
+
LayerCake needs mainly:
|
|
127
|
+
|
|
128
|
+
* [Numpy](https://numpy.org/) for numeric support
|
|
129
|
+
* [sparse](https://sparse.pydata.org/) for sparse multidimensional arrays support
|
|
130
|
+
* [Numba](https://numba.pydata.org/) for code acceleration
|
|
131
|
+
* [Sympy](https://www.sympy.org/) for symbolic manipulation of inner products
|
|
132
|
+
|
|
133
|
+
Check the YAML file [environment.yml](https://raw.githubusercontent.com/Climdyn/LayerCake/main/environment.yml) for the dependencies.
|
|
134
|
+
|
|
135
|
+
## Contributing
|
|
136
|
+
|
|
137
|
+
LayerCake is in beta development phase, bug reports and tests of the features are welcome.
|
|
138
|
+
Please simply raise an issue on [Github](https://github.com/Climdyn/LayerCake/issues).
|
|
139
|
+
|
|
140
|
+
If you want to contribute actively to the development, please contact the main authors.
|
|
141
|
+
In addition, if you have made changes that you think will be useful to others, please feel free to suggest these as a pull request
|
|
142
|
+
on the [LayerCake Github repository](https://github.com/Climdyn/LayerCake/pulls).
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# LayerCake
|
|
2
|
+
|
|
3
|
+
> “Welcome to the layer cake son.”
|
|
4
|
+
>
|
|
5
|
+
> -- *Michael Gambon - Layer Cake (2004)*
|
|
6
|
+
|
|
7
|
+
[](https://doi.org/10.5281/zenodo.19789922)
|
|
8
|
+
[](https://climdyn.github.io/LayerCake/)
|
|
9
|
+
|
|
10
|
+
## General Information
|
|
11
|
+
|
|
12
|
+
LayerCake is a framework to design models based on systems of partial differential equations (PDEs),
|
|
13
|
+
and convert them to ordinary differential equations (ODEs) via Galerkin-type expansions.
|
|
14
|
+
|
|
15
|
+
LayerCake allows you to construct systems of PDEs, and to specify coordinate systems and basis functions to build
|
|
16
|
+
the corresponding ODE systems.
|
|
17
|
+
To build these systems, LayerCake relies heavily on the [Sympy](https://www.sympy.org/) symbolic computation framework.
|
|
18
|
+
The output of this procedure is either a [Numbaified](https://numba.pydata.org/) Python callable,
|
|
19
|
+
or a list of symbolic ODE tendencies that can be integrated in any of the supported languages
|
|
20
|
+
(Fortran, Julia and Python for the moment). These two kinds of output allow for the study of the computed models with
|
|
21
|
+
the modern tools available in all these languages.
|
|
22
|
+
|
|
23
|
+
LayerCake has been designed with geophysics in mind, although it may be useful for other applications.
|
|
24
|
+
|
|
25
|
+
## About
|
|
26
|
+
|
|
27
|
+
(c) 2025-2026 Jonathan Demaeyer and Oisín Hamilton
|
|
28
|
+
|
|
29
|
+
See [LICENSE.txt](https://github.com/Climdyn/LayerCake/blob/main/LICENSE.txt) for license information.
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
### With pip
|
|
34
|
+
|
|
35
|
+
The easiest way to install and run LayerCake is to use [pip](https://pypi.org/).
|
|
36
|
+
Type in a terminal
|
|
37
|
+
|
|
38
|
+
pip install layercake-model
|
|
39
|
+
|
|
40
|
+
and you are set!
|
|
41
|
+
|
|
42
|
+
Additionally, you can clone the repository
|
|
43
|
+
|
|
44
|
+
git clone https://github.com/Climdyn/LayerCake.git
|
|
45
|
+
|
|
46
|
+
and perform a test by running the script
|
|
47
|
+
|
|
48
|
+
python examples/atmospheric/barotropic_one_layer.py
|
|
49
|
+
|
|
50
|
+
to see if everything runs smoothly (this should take less than 5 minutes).
|
|
51
|
+
|
|
52
|
+
### With Anaconda
|
|
53
|
+
|
|
54
|
+
The second-easiest way to install and run LayerCake is to use an appropriate environment
|
|
55
|
+
created through [Anaconda](https://www.anaconda.com/).
|
|
56
|
+
|
|
57
|
+
First install Anaconda and clone the repository:
|
|
58
|
+
|
|
59
|
+
git clone https://github.com/jodemaey/LayerCake.git
|
|
60
|
+
|
|
61
|
+
Then install and activate the Python3 Anaconda environment:
|
|
62
|
+
|
|
63
|
+
conda env create -f environment.yml
|
|
64
|
+
conda activate layercake
|
|
65
|
+
|
|
66
|
+
You can then perform a test by running the script
|
|
67
|
+
|
|
68
|
+
python examples/atmospheric/barotropic_one_layer.py
|
|
69
|
+
|
|
70
|
+
to see if everything runs smoothly (this should take less than 5 minutes to run).
|
|
71
|
+
|
|
72
|
+
## Documentation
|
|
73
|
+
|
|
74
|
+
To build the documentation, please run (with the conda environment activated):
|
|
75
|
+
|
|
76
|
+
cd documentation
|
|
77
|
+
make html
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
You may need to install [make](https://www.gnu.org/software/make/) if it is not already present on your system.
|
|
81
|
+
Once built, the documentation is available [here](./documentation/build/html/index.html).
|
|
82
|
+
|
|
83
|
+
The documentation is also available online at https://climdyn.github.io/LayerCake/. In particular,
|
|
84
|
+
please consider reading the [User guide](https://climdyn.github.io/LayerCake/files/user_guide.html#).
|
|
85
|
+
|
|
86
|
+
## Examples
|
|
87
|
+
|
|
88
|
+
A few examples are available in the [examples](./examples) folder. More examples will be provided as the code is
|
|
89
|
+
further developed.
|
|
90
|
+
|
|
91
|
+
## Dependencies
|
|
92
|
+
|
|
93
|
+
LayerCake needs mainly:
|
|
94
|
+
|
|
95
|
+
* [Numpy](https://numpy.org/) for numeric support
|
|
96
|
+
* [sparse](https://sparse.pydata.org/) for sparse multidimensional arrays support
|
|
97
|
+
* [Numba](https://numba.pydata.org/) for code acceleration
|
|
98
|
+
* [Sympy](https://www.sympy.org/) for symbolic manipulation of inner products
|
|
99
|
+
|
|
100
|
+
Check the YAML file [environment.yml](https://raw.githubusercontent.com/Climdyn/LayerCake/main/environment.yml) for the dependencies.
|
|
101
|
+
|
|
102
|
+
## Contributing
|
|
103
|
+
|
|
104
|
+
LayerCake is in beta development phase, bug reports and tests of the features are welcome.
|
|
105
|
+
Please simply raise an issue on [Github](https://github.com/Climdyn/LayerCake/issues).
|
|
106
|
+
|
|
107
|
+
If you want to contribute actively to the development, please contact the main authors.
|
|
108
|
+
In addition, if you have made changes that you think will be useful to others, please feel free to suggest these as a pull request
|
|
109
|
+
on the [LayerCake Github repository](https://github.com/Climdyn/LayerCake/pulls).
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
|
|
2
|
+
from .variables.parameter import Parameter
|
|
3
|
+
from .variables.field import Field, ParameterField, FunctionField
|
|
4
|
+
from .basis.base import SymbolicBasis
|
|
5
|
+
from .arithmetic.symbolic.expressions import Expression
|
|
6
|
+
from .arithmetic.terms import *
|
|
7
|
+
from .arithmetic.equation import Equation
|
|
8
|
+
from .arithmetic.symbolic.operators import Laplacian, D
|
|
9
|
+
from .bakery.layers import Layer
|
|
10
|
+
from .bakery.cake import Cake
|
|
11
|
+
|
|
12
|
+
__all__ = ['Parameter', 'ParameterField', 'FunctionField', 'Field', 'Expression', 'SymbolicBasis', 'vorticity_advection', 'Jacobian',
|
|
13
|
+
'OperatorTerm', 'ProductOfTerms', 'AdditionOfTerms', 'LinearTerm', 'ConstantTerm', 'Equation', 'Laplacian', 'D',
|
|
14
|
+
'Layer', 'Cake']
|
|
15
|
+
|
|
16
|
+
__version__ = '1.0.2a0'
|
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
|
|
2
|
+
"""
|
|
3
|
+
|
|
4
|
+
Equation definition module
|
|
5
|
+
==========================
|
|
6
|
+
|
|
7
|
+
Main class to define partial differential equation.
|
|
8
|
+
This class is the workhorse of LayerCake to define and specify
|
|
9
|
+
partial differential equation.
|
|
10
|
+
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from sympy import Symbol, S, Eq
|
|
14
|
+
import matplotlib.pyplot as plt
|
|
15
|
+
from layercake.arithmetic.terms.base import ArithmeticTerms
|
|
16
|
+
from layercake.utils import isin
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# TODO: deal with the cases where lists of terms are empty - maybe nothing is needed but to check
|
|
20
|
+
|
|
21
|
+
class Equation(object):
|
|
22
|
+
"""Main class to define and specify partial differential equations.
|
|
23
|
+
|
|
24
|
+
Notes
|
|
25
|
+
-----
|
|
26
|
+
The left-hand side is always expressed as a partial time derivative of
|
|
27
|
+
something: :math:`\\partial_t` .
|
|
28
|
+
|
|
29
|
+
Parameters
|
|
30
|
+
----------
|
|
31
|
+
field: ~field.Field
|
|
32
|
+
The spatial field over which the partial differential equation.
|
|
33
|
+
lhs_terms: ~arithmetic.terms.base.ArithmeticTerms or list(~arithmetic.terms.base.ArithmeticTerms), optional
|
|
34
|
+
Terms on the left-hand side of the equation. At least one must involve the field defined above.
|
|
35
|
+
name: str, optional
|
|
36
|
+
Name for the equation.
|
|
37
|
+
|
|
38
|
+
Attributes
|
|
39
|
+
----------
|
|
40
|
+
field: ~field.Field
|
|
41
|
+
The spatial field over which the partial differential equation.
|
|
42
|
+
rhs_terms: list(~arithmetic.terms.base.ArithmeticTerms)
|
|
43
|
+
List of additive terms in the right-hand side of the equation.
|
|
44
|
+
lhs_terms: ListOfArithmeticTerms(~arithmetic.terms.base.ArithmeticTerms)
|
|
45
|
+
Term on the left-hand side of the equation.
|
|
46
|
+
name: str
|
|
47
|
+
Optional name for the equation.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
_t = Symbol('t')
|
|
51
|
+
|
|
52
|
+
def __init__(self, field, lhs_terms=None, name=''):
|
|
53
|
+
|
|
54
|
+
self.field = field
|
|
55
|
+
self.field._equation = self
|
|
56
|
+
self.rhs_terms = ListOfAdditiveArithmeticTerms()
|
|
57
|
+
if lhs_terms is None:
|
|
58
|
+
self.lhs_terms = ListOfAdditiveArithmeticTerms()
|
|
59
|
+
elif isinstance(lhs_terms, list):
|
|
60
|
+
self.lhs_terms = ListOfAdditiveArithmeticTerms(lhs_terms)
|
|
61
|
+
else:
|
|
62
|
+
self.lhs_terms = ListOfAdditiveArithmeticTerms([lhs_terms])
|
|
63
|
+
self.name = name
|
|
64
|
+
self._layer = None
|
|
65
|
+
self._cake = None
|
|
66
|
+
|
|
67
|
+
def add_rhs_term(self, term):
|
|
68
|
+
"""Add a term to the right-hand side of the equation.
|
|
69
|
+
|
|
70
|
+
Parameters
|
|
71
|
+
----------
|
|
72
|
+
term: ~arithmetic.terms.base.ArithmeticTerms
|
|
73
|
+
Term to be added to the right-hand side of the equation.
|
|
74
|
+
"""
|
|
75
|
+
if not issubclass(term.__class__, ArithmeticTerms):
|
|
76
|
+
raise ValueError('Provided term must be a valid ArithmeticTerm object.')
|
|
77
|
+
self.terms.append(term)
|
|
78
|
+
|
|
79
|
+
def add_rhs_terms(self, terms):
|
|
80
|
+
"""Add multiple terms to the right-hand side of the equation.
|
|
81
|
+
|
|
82
|
+
Parameters
|
|
83
|
+
----------
|
|
84
|
+
terms: list(~arithmetic.terms.base.ArithmeticTerms)
|
|
85
|
+
Terms to be added to the right-hand side of the equation.
|
|
86
|
+
"""
|
|
87
|
+
for t in terms:
|
|
88
|
+
self.add_rhs_term(t)
|
|
89
|
+
|
|
90
|
+
def add_lhs_term(self, term):
|
|
91
|
+
"""Add a term to the left-hand side of the equation.
|
|
92
|
+
|
|
93
|
+
Parameters
|
|
94
|
+
----------
|
|
95
|
+
term: ~arithmetic.terms.base.ArithmeticTerms
|
|
96
|
+
Term to be added to the left-hand side of the equation.
|
|
97
|
+
"""
|
|
98
|
+
if not issubclass(term.__class__, ArithmeticTerms):
|
|
99
|
+
raise ValueError('Provided term must be a valid ArithmeticTerm object.')
|
|
100
|
+
self.lhs_terms.append(term)
|
|
101
|
+
|
|
102
|
+
def add_lhs_terms(self, terms):
|
|
103
|
+
"""Add multiple terms to the left-hand side of the equation.
|
|
104
|
+
|
|
105
|
+
Parameters
|
|
106
|
+
----------
|
|
107
|
+
terms: list(~arithmetic.terms.base.ArithmeticTerms)
|
|
108
|
+
Terms to be added to the left-hand side of the equation.
|
|
109
|
+
"""
|
|
110
|
+
for t in terms:
|
|
111
|
+
self.add_lhs_term(t)
|
|
112
|
+
|
|
113
|
+
@property
|
|
114
|
+
def terms(self):
|
|
115
|
+
"""Alias for the list of RHS arithmetic terms."""
|
|
116
|
+
return self.rhs_terms
|
|
117
|
+
|
|
118
|
+
@property
|
|
119
|
+
def other_fields(self):
|
|
120
|
+
"""list(~field.Field): List of additional fields present in the equation."""
|
|
121
|
+
other_fields = list()
|
|
122
|
+
for equation_term in self.terms:
|
|
123
|
+
for term in equation_term.terms:
|
|
124
|
+
if term.field is not self.field and term.field.dynamical and term.field not in other_fields:
|
|
125
|
+
other_fields.append(term.field)
|
|
126
|
+
other_fields = other_fields + self.other_fields_in_lhs
|
|
127
|
+
return other_fields
|
|
128
|
+
|
|
129
|
+
@property
|
|
130
|
+
def other_fields_in_lhs(self):
|
|
131
|
+
"""list(~field.Field): List of additional fields present in the LHS of the equation."""
|
|
132
|
+
other_fields = list()
|
|
133
|
+
for equation_term in self.lhs_terms:
|
|
134
|
+
for term in equation_term.terms:
|
|
135
|
+
if term.field is not self.field and term.field.dynamical and term.field not in other_fields:
|
|
136
|
+
other_fields.append(term.field)
|
|
137
|
+
return other_fields
|
|
138
|
+
|
|
139
|
+
@property
|
|
140
|
+
def parameter_fields(self):
|
|
141
|
+
"""list(~field.ParameterField): List of non-dynamical parameter fields present in the equation."""
|
|
142
|
+
parameter_fields = list()
|
|
143
|
+
for equation_term in self.terms:
|
|
144
|
+
for term in equation_term.terms:
|
|
145
|
+
if term.field is not self.field and not term.field.dynamical and term.field not in parameter_fields:
|
|
146
|
+
parameter_fields.append(term.field)
|
|
147
|
+
for equation_term in self.lhs_terms:
|
|
148
|
+
for term in equation_term.terms:
|
|
149
|
+
if term.field is not self.field and not term.field.dynamical and term.field not in parameter_fields:
|
|
150
|
+
parameter_fields.append(term.field)
|
|
151
|
+
return parameter_fields
|
|
152
|
+
|
|
153
|
+
@property
|
|
154
|
+
def parameters(self):
|
|
155
|
+
"""list(~parameter.Parameter): List of parameters present in the equation."""
|
|
156
|
+
parameters_list = list()
|
|
157
|
+
for term in self.terms + self.lhs_terms:
|
|
158
|
+
params_list = term.parameters
|
|
159
|
+
for param in params_list:
|
|
160
|
+
if not isin(param, parameters_list):
|
|
161
|
+
parameters_list.append(param)
|
|
162
|
+
|
|
163
|
+
for param_field in self.parameter_fields:
|
|
164
|
+
for param in param_field.parameters:
|
|
165
|
+
if param is not None and not isin(param, parameters_list):
|
|
166
|
+
parameters_list.append(param)
|
|
167
|
+
|
|
168
|
+
return parameters_list
|
|
169
|
+
|
|
170
|
+
@property
|
|
171
|
+
def parameters_symbols(self):
|
|
172
|
+
"""list(~sympy.core.symbol.Symbol): List of parameter's symbols present in the equation."""
|
|
173
|
+
return [p.symbol for p in self.parameters]
|
|
174
|
+
|
|
175
|
+
@property
|
|
176
|
+
def symbolic_expression(self):
|
|
177
|
+
"""~sympy.core.expr.Expr: Symbolic expression of the equation."""
|
|
178
|
+
return Eq(self.symbolic_lhs.diff(self._t, evaluate=False), self.rhs_terms.symbolic_expression)
|
|
179
|
+
|
|
180
|
+
@property
|
|
181
|
+
def numerical_expression(self):
|
|
182
|
+
"""~sympy.core.expr.Expr: Expression of the equation with parameters replaced by their
|
|
183
|
+
configured values."""
|
|
184
|
+
return Eq(self.numerical_lhs.diff(self._t, evaluate=False), self.rhs_terms.symbolic_expression)
|
|
185
|
+
|
|
186
|
+
@property
|
|
187
|
+
def symbolic_rhs(self):
|
|
188
|
+
"""~sympy.core.expr.Expr: Symbolic expression of the right-hand side of the equation."""
|
|
189
|
+
return self.rhs_terms.symbolic_expression
|
|
190
|
+
|
|
191
|
+
@property
|
|
192
|
+
def numerical_rhs(self):
|
|
193
|
+
"""~sympy.core.expr.Expr: Expression of the right-hand side of the equation with
|
|
194
|
+
parameters replaced by their configured values."""
|
|
195
|
+
return self.rhs_terms.numerical_expression
|
|
196
|
+
|
|
197
|
+
@property
|
|
198
|
+
def symbolic_lhs(self):
|
|
199
|
+
"""~sympy.core.expr.Expr: Symbolic expression of the left-hand side of the equation."""
|
|
200
|
+
return self.lhs_terms.symbolic_expression
|
|
201
|
+
|
|
202
|
+
@property
|
|
203
|
+
def numerical_lhs(self):
|
|
204
|
+
"""~sympy.core.expr.Expr: Expression of the left-hand side of the equation with
|
|
205
|
+
parameters replaced by their configured values."""
|
|
206
|
+
return self.lhs_terms.numerical_expression
|
|
207
|
+
|
|
208
|
+
@property
|
|
209
|
+
def lhs_inner_products(self):
|
|
210
|
+
"""list(~sympy.matrices.immutable.ImmutableSparseMatrix or ~sympy.tensor.array.ImmutableSparseNDimArray) or list(sparse.COO(float)): Inner products of each term of
|
|
211
|
+
the left-hand side of the equation, if available."""
|
|
212
|
+
return [term.inner_products for term in self.lhs_terms]
|
|
213
|
+
|
|
214
|
+
@property
|
|
215
|
+
def lhs_inner_products_addition(self):
|
|
216
|
+
"""~sympy.matrices.immutable.ImmutableSparseMatrix or ~sympy.tensor.array.ImmutableSparseNDimArray or sparse.COO(float): Added left-hand
|
|
217
|
+
side inner products of the equation, if available. Might raise an error if not all terms are compatible."""
|
|
218
|
+
result = self.lhs_terms[0].inner_products.copy()
|
|
219
|
+
for term in self.lhs_terms[1:]:
|
|
220
|
+
result = result + term.inner_products
|
|
221
|
+
return result
|
|
222
|
+
|
|
223
|
+
@property
|
|
224
|
+
def maximum_rank(self):
|
|
225
|
+
"""int: Maximum rank of the right-hand side terms tensors."""
|
|
226
|
+
return max(self.terms.maximum_rank, self.lhs_terms.maximum_rank)
|
|
227
|
+
|
|
228
|
+
def compute_inner_products(self, basis, numerical=False, timeout=None, num_threads=None, permute=False):
|
|
229
|
+
"""Compute the inner products tensor of the left-hand and right-hand side terms.
|
|
230
|
+
|
|
231
|
+
Parameters
|
|
232
|
+
----------
|
|
233
|
+
basis: SymbolicBasis
|
|
234
|
+
Basis with which to compute the inner products.
|
|
235
|
+
numerical: bool, optional
|
|
236
|
+
Whether the resulting computed inner products must be numerical or symbolic.
|
|
237
|
+
Default to `False`, i.e. symbolic output.
|
|
238
|
+
num_threads: int or None, optional
|
|
239
|
+
Number of threads to use to compute the inner products. If `None` use all the cpus available.
|
|
240
|
+
Default to `None`.
|
|
241
|
+
timeout: int or float or bool or None, optional
|
|
242
|
+
Control the switch from symbolic to numerical integration. By default, `parallel_integration` workers will try to integrate
|
|
243
|
+
|Sympy| expressions symbolically, but a fallback to numerical integration can be enforced.
|
|
244
|
+
The options are:
|
|
245
|
+
|
|
246
|
+
* `None`: This is the "full-symbolic" mode. No timeout will be applied, and the switch to numerical integration will never happen.
|
|
247
|
+
Can result in very long and improbable computation time.
|
|
248
|
+
* `True`: This is the "full-numerical" mode. Symbolic computations do not occur, and the workers try directly to integrate
|
|
249
|
+
numerically.
|
|
250
|
+
* `False`: Same as `None`.
|
|
251
|
+
* An integer: defines a timeout after which, if a symbolic integration have not completed, the worker switch to the
|
|
252
|
+
numerical integration.
|
|
253
|
+
permute: bool, optional
|
|
254
|
+
If `True`, applies all the possible permutations to the tensor indices
|
|
255
|
+
from 1 to the rank of the tensor.
|
|
256
|
+
Default to `False`, i.e. no permutation is applied.
|
|
257
|
+
"""
|
|
258
|
+
self.lhs_terms.compute_inner_products(basis, numerical, timeout, num_threads, permute)
|
|
259
|
+
self.rhs_terms.compute_inner_products(basis, numerical, timeout, num_threads, permute)
|
|
260
|
+
|
|
261
|
+
def to_latex(self, enclose_lhs=True, drop_first_lhs_char=True, drop_first_rhs_char=False):
|
|
262
|
+
"""Generate a LaTeX string representing the equation mathematically.
|
|
263
|
+
|
|
264
|
+
Parameters
|
|
265
|
+
----------
|
|
266
|
+
enclose_lhs: bool, optional
|
|
267
|
+
Whether to enclose the left-hand side term inside parenthesis.
|
|
268
|
+
Default to `True`.
|
|
269
|
+
drop_first_lhs_char: bool, optional
|
|
270
|
+
Whether to drop the first two character of the left-hand side latex string.
|
|
271
|
+
Useful to drop the sign in front of it.
|
|
272
|
+
Default to `True`.
|
|
273
|
+
drop_first_rhs_char: bool, optional
|
|
274
|
+
Whether to drop the first two character of the right-hand side latex string.
|
|
275
|
+
Useful to drop the sign in front of it.
|
|
276
|
+
Default to `False`.
|
|
277
|
+
|
|
278
|
+
Returns
|
|
279
|
+
-------
|
|
280
|
+
str
|
|
281
|
+
The LaTeX string representing the equation.
|
|
282
|
+
"""
|
|
283
|
+
lhs = self.lhs_terms[0].latex
|
|
284
|
+
for term in self.lhs_terms[1:]:
|
|
285
|
+
lhs += term.latex
|
|
286
|
+
if drop_first_lhs_char:
|
|
287
|
+
lhs = lhs[2:]
|
|
288
|
+
if enclose_lhs:
|
|
289
|
+
latex_string = r'\frac{\partial}{\partial t} ' + r'\left(' + lhs + r'\right)'
|
|
290
|
+
else:
|
|
291
|
+
latex_string = r'\frac{\partial}{\partial t} ' + lhs
|
|
292
|
+
|
|
293
|
+
first_term = self.terms[0].latex
|
|
294
|
+
if drop_first_rhs_char:
|
|
295
|
+
first_term = first_term[2:]
|
|
296
|
+
latex_string += ' = ' + first_term
|
|
297
|
+
|
|
298
|
+
for term in self.terms[1:]:
|
|
299
|
+
latex_string += term.latex
|
|
300
|
+
|
|
301
|
+
return latex_string
|
|
302
|
+
|
|
303
|
+
def show_latex(self, enclose_lhs=True, drop_first_lhs_char=True, drop_first_rhs_char=False):
|
|
304
|
+
"""Show the LaTeX string representing the equation mathematically rendered in a window.
|
|
305
|
+
|
|
306
|
+
Parameters
|
|
307
|
+
----------
|
|
308
|
+
enclose_lhs: bool, optional
|
|
309
|
+
Whether to enclose the left-hand side term inside parenthesis.
|
|
310
|
+
Default to `True`.
|
|
311
|
+
drop_first_lhs_char: bool, optional
|
|
312
|
+
Whether to drop the first two character of the left-hand side latex string.
|
|
313
|
+
Useful to drop the sign in front of it.
|
|
314
|
+
Default to `True`.
|
|
315
|
+
drop_first_rhs_char: bool, optional
|
|
316
|
+
Whether to drop the first two character of the right-hand side latex string.
|
|
317
|
+
Useful to drop the sign in front of it.
|
|
318
|
+
Default to `False`.
|
|
319
|
+
"""
|
|
320
|
+
|
|
321
|
+
latex_string = self.to_latex(enclose_lhs=enclose_lhs,
|
|
322
|
+
drop_first_lhs_char=drop_first_lhs_char,
|
|
323
|
+
drop_first_rhs_char=drop_first_rhs_char
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
plt.figure(figsize=(8, 2))
|
|
327
|
+
plt.axis('off')
|
|
328
|
+
plt.text(-0.1, 0.5, '$%s$' % latex_string)
|
|
329
|
+
plt.show()
|
|
330
|
+
|
|
331
|
+
def __repr__(self):
|
|
332
|
+
eq = self.symbolic_expression
|
|
333
|
+
return f'{eq.lhs} = {eq.rhs}'
|
|
334
|
+
|
|
335
|
+
def __str__(self):
|
|
336
|
+
return self.__repr__()
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
class ListOfAdditiveArithmeticTerms(list):
|
|
340
|
+
"""Class holding list of additive arithmetic terms in equations."""
|
|
341
|
+
|
|
342
|
+
def compute_inner_products(self, basis, numerical=False, timeout=None, num_threads=None, permute=False):
|
|
343
|
+
"""Compute the inner products tensor of the all the terms of the list.
|
|
344
|
+
|
|
345
|
+
Parameters
|
|
346
|
+
----------
|
|
347
|
+
basis: SymbolicBasis
|
|
348
|
+
Basis with which to compute the inner products.
|
|
349
|
+
numerical: bool, optional
|
|
350
|
+
Whether the resulting computed inner products must be numerical or symbolic.
|
|
351
|
+
Default to `False`, i.e. symbolic output.
|
|
352
|
+
num_threads: int or None, optional
|
|
353
|
+
Number of threads to use to compute the inner products. If `None` use all the cpus available.
|
|
354
|
+
Default to `None`.
|
|
355
|
+
timeout: int or float or bool or None, optional
|
|
356
|
+
Control the switch from symbolic to numerical integration. By default, `parallel_integration` workers will try to integrate
|
|
357
|
+
|Sympy| expressions symbolically, but a fallback to numerical integration can be enforced.
|
|
358
|
+
The options are:
|
|
359
|
+
|
|
360
|
+
* `None`: This is the "full-symbolic" mode. No timeout will be applied, and the switch to numerical integration will never happen.
|
|
361
|
+
Can result in very long and improbable computation time.
|
|
362
|
+
* `True`: This is the "full-numerical" mode. Symbolic computations do not occur, and the workers try directly to integrate
|
|
363
|
+
numerically.
|
|
364
|
+
* `False`: Same as `None`.
|
|
365
|
+
* An integer: defines a timeout after which, if a symbolic integration have not completed, the worker switch to the
|
|
366
|
+
numerical integration.
|
|
367
|
+
permute: bool, optional
|
|
368
|
+
If `True`, applies all the possible permutations to the tensor indices
|
|
369
|
+
from 1 to the rank of the tensor.
|
|
370
|
+
Default to `False`, i.e. no permutation is applied.
|
|
371
|
+
"""
|
|
372
|
+
|
|
373
|
+
for term in self:
|
|
374
|
+
term.compute_inner_products(basis, numerical, timeout, num_threads, permute)
|
|
375
|
+
|
|
376
|
+
@property
|
|
377
|
+
def maximum_rank(self):
|
|
378
|
+
"""int: Maximum rank of the right-hand side terms tensors."""
|
|
379
|
+
max_rank = 0
|
|
380
|
+
for term in self:
|
|
381
|
+
max_rank = max(max_rank, term.rank)
|
|
382
|
+
return max_rank
|
|
383
|
+
|
|
384
|
+
@property
|
|
385
|
+
def same_rank(self):
|
|
386
|
+
"""bool: Check if all terms have the same rank."""
|
|
387
|
+
rank = self[0].rank
|
|
388
|
+
for term in self[1:]:
|
|
389
|
+
if term.rank != rank:
|
|
390
|
+
return False
|
|
391
|
+
return True
|
|
392
|
+
|
|
393
|
+
@property
|
|
394
|
+
def symbolic_expression(self):
|
|
395
|
+
"""~sympy.core.expr.Expr: Symbolic expression of the collection of additive arithmetic terms."""
|
|
396
|
+
|
|
397
|
+
rterm = S.Zero
|
|
398
|
+
for term in self:
|
|
399
|
+
rterm += term.symbolic_expression
|
|
400
|
+
return rterm
|
|
401
|
+
|
|
402
|
+
@property
|
|
403
|
+
def numerical_expression(self):
|
|
404
|
+
"""~sympy.core.expr.Expr: Expression of the collection of additive arithmetic terms with
|
|
405
|
+
parameters replaced by their configured values."""
|
|
406
|
+
|
|
407
|
+
rterm = S.Zero
|
|
408
|
+
for term in self:
|
|
409
|
+
rterm += term.numerical_expression
|
|
410
|
+
return rterm
|