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.
Files changed (57) hide show
  1. layercake_model-1.0.2a0/LICENSE.txt +11 -0
  2. layercake_model-1.0.2a0/PKG-INFO +142 -0
  3. layercake_model-1.0.2a0/README.md +109 -0
  4. layercake_model-1.0.2a0/layercake/__init__.py +16 -0
  5. layercake_model-1.0.2a0/layercake/arithmetic/equation.py +410 -0
  6. layercake_model-1.0.2a0/layercake/arithmetic/symbolic/__init__.py +0 -0
  7. layercake_model-1.0.2a0/layercake/arithmetic/symbolic/expressions.py +73 -0
  8. layercake_model-1.0.2a0/layercake/arithmetic/symbolic/operators.py +338 -0
  9. layercake_model-1.0.2a0/layercake/arithmetic/terms/__init__.py +9 -0
  10. layercake_model-1.0.2a0/layercake/arithmetic/terms/base.py +884 -0
  11. layercake_model-1.0.2a0/layercake/arithmetic/terms/constant.py +119 -0
  12. layercake_model-1.0.2a0/layercake/arithmetic/terms/gradient.py +185 -0
  13. layercake_model-1.0.2a0/layercake/arithmetic/terms/jacobian.py +183 -0
  14. layercake_model-1.0.2a0/layercake/arithmetic/terms/linear.py +98 -0
  15. layercake_model-1.0.2a0/layercake/arithmetic/terms/operations.py +262 -0
  16. layercake_model-1.0.2a0/layercake/arithmetic/terms/operators.py +226 -0
  17. layercake_model-1.0.2a0/layercake/arithmetic/utils.py +20 -0
  18. layercake_model-1.0.2a0/layercake/bakery/__init__.py +0 -0
  19. layercake_model-1.0.2a0/layercake/bakery/cake.py +705 -0
  20. layercake_model-1.0.2a0/layercake/bakery/layers.py +569 -0
  21. layercake_model-1.0.2a0/layercake/basis/__init__.py +4 -0
  22. layercake_model-1.0.2a0/layercake/basis/base.py +244 -0
  23. layercake_model-1.0.2a0/layercake/basis/planar_fourier.py +418 -0
  24. layercake_model-1.0.2a0/layercake/basis/spherical_harmonics.py +166 -0
  25. layercake_model-1.0.2a0/layercake/formatters/__init__.py +0 -0
  26. layercake_model-1.0.2a0/layercake/formatters/base.py +183 -0
  27. layercake_model-1.0.2a0/layercake/formatters/fortran.py +85 -0
  28. layercake_model-1.0.2a0/layercake/formatters/julia.py +93 -0
  29. layercake_model-1.0.2a0/layercake/formatters/python.py +89 -0
  30. layercake_model-1.0.2a0/layercake/inner_products/__init__.py +0 -0
  31. layercake_model-1.0.2a0/layercake/inner_products/definition.py +218 -0
  32. layercake_model-1.0.2a0/layercake/utils/__init__.py +8 -0
  33. layercake_model-1.0.2a0/layercake/utils/commutativity.py +45 -0
  34. layercake_model-1.0.2a0/layercake/utils/integration.py +200 -0
  35. layercake_model-1.0.2a0/layercake/utils/matrix.py +104 -0
  36. layercake_model-1.0.2a0/layercake/utils/parallel.py +215 -0
  37. layercake_model-1.0.2a0/layercake/utils/symbolic_tensor.py +172 -0
  38. layercake_model-1.0.2a0/layercake/utils/tensor.py +83 -0
  39. layercake_model-1.0.2a0/layercake/variables/__init__.py +0 -0
  40. layercake_model-1.0.2a0/layercake/variables/coordinate.py +65 -0
  41. layercake_model-1.0.2a0/layercake/variables/field.py +535 -0
  42. layercake_model-1.0.2a0/layercake/variables/parameter.py +664 -0
  43. layercake_model-1.0.2a0/layercake/variables/systems.py +143 -0
  44. layercake_model-1.0.2a0/layercake/variables/utils.py +142 -0
  45. layercake_model-1.0.2a0/layercake/variables/variable.py +314 -0
  46. layercake_model-1.0.2a0/layercake_model.egg-info/PKG-INFO +142 -0
  47. layercake_model-1.0.2a0/layercake_model.egg-info/SOURCES.txt +55 -0
  48. layercake_model-1.0.2a0/layercake_model.egg-info/dependency_links.txt +1 -0
  49. layercake_model-1.0.2a0/layercake_model.egg-info/requires.txt +7 -0
  50. layercake_model-1.0.2a0/layercake_model.egg-info/top_level.txt +1 -0
  51. layercake_model-1.0.2a0/pyproject.toml +61 -0
  52. layercake_model-1.0.2a0/setup.cfg +4 -0
  53. layercake_model-1.0.2a0/test/test_RP1982_qgs_numerical.py +330 -0
  54. layercake_model-1.0.2a0/test/test_RP1982_qgs_symbolic.py +340 -0
  55. layercake_model-1.0.2a0/test/test_base.py +82 -0
  56. layercake_model-1.0.2a0/test/test_maooam_qgs_numerical.py +444 -0
  57. 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
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.19789922.svg)](https://doi.org/10.5281/zenodo.19789922)
41
+ [![Documentation Status](https://img.shields.io/badge/docs-passing-green.svg)](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
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.19789922.svg)](https://doi.org/10.5281/zenodo.19789922)
8
+ [![Documentation Status](https://img.shields.io/badge/docs-passing-green.svg)](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