layercake-model 1.0.2a0__py3-none-any.whl

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 (47) hide show
  1. layercake/__init__.py +16 -0
  2. layercake/arithmetic/equation.py +410 -0
  3. layercake/arithmetic/symbolic/__init__.py +0 -0
  4. layercake/arithmetic/symbolic/expressions.py +73 -0
  5. layercake/arithmetic/symbolic/operators.py +338 -0
  6. layercake/arithmetic/terms/__init__.py +9 -0
  7. layercake/arithmetic/terms/base.py +884 -0
  8. layercake/arithmetic/terms/constant.py +119 -0
  9. layercake/arithmetic/terms/gradient.py +185 -0
  10. layercake/arithmetic/terms/jacobian.py +183 -0
  11. layercake/arithmetic/terms/linear.py +98 -0
  12. layercake/arithmetic/terms/operations.py +262 -0
  13. layercake/arithmetic/terms/operators.py +226 -0
  14. layercake/arithmetic/utils.py +20 -0
  15. layercake/bakery/__init__.py +0 -0
  16. layercake/bakery/cake.py +705 -0
  17. layercake/bakery/layers.py +569 -0
  18. layercake/basis/__init__.py +4 -0
  19. layercake/basis/base.py +244 -0
  20. layercake/basis/planar_fourier.py +418 -0
  21. layercake/basis/spherical_harmonics.py +166 -0
  22. layercake/formatters/__init__.py +0 -0
  23. layercake/formatters/base.py +183 -0
  24. layercake/formatters/fortran.py +85 -0
  25. layercake/formatters/julia.py +93 -0
  26. layercake/formatters/python.py +89 -0
  27. layercake/inner_products/__init__.py +0 -0
  28. layercake/inner_products/definition.py +218 -0
  29. layercake/utils/__init__.py +8 -0
  30. layercake/utils/commutativity.py +45 -0
  31. layercake/utils/integration.py +200 -0
  32. layercake/utils/matrix.py +104 -0
  33. layercake/utils/parallel.py +215 -0
  34. layercake/utils/symbolic_tensor.py +172 -0
  35. layercake/utils/tensor.py +83 -0
  36. layercake/variables/__init__.py +0 -0
  37. layercake/variables/coordinate.py +65 -0
  38. layercake/variables/field.py +535 -0
  39. layercake/variables/parameter.py +664 -0
  40. layercake/variables/systems.py +143 -0
  41. layercake/variables/utils.py +142 -0
  42. layercake/variables/variable.py +314 -0
  43. layercake_model-1.0.2a0.dist-info/METADATA +142 -0
  44. layercake_model-1.0.2a0.dist-info/RECORD +47 -0
  45. layercake_model-1.0.2a0.dist-info/WHEEL +5 -0
  46. layercake_model-1.0.2a0.dist-info/licenses/LICENSE.txt +11 -0
  47. layercake_model-1.0.2a0.dist-info/top_level.txt +1 -0
layercake/__init__.py ADDED
@@ -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
File without changes
@@ -0,0 +1,73 @@
1
+
2
+ """
3
+ Expressions definition module
4
+ =============================
5
+
6
+ This module defines mathematical expression to be inserted in the models equations.
7
+
8
+ """
9
+
10
+
11
+ class Expression(object):
12
+ """ Class defining a general mathematical expression in the equations.
13
+ Can be used for example as prefactor for arithmetic terms.
14
+
15
+ Parameters
16
+ ----------
17
+ symbolic_expression: ~sympy.core.expr.Expr
18
+ A |Sympy| expression to represent the field mathematical expression in symbolic expressions.
19
+ expression_parameters: None or list(~parameter.Parameter), optional
20
+ List of parameters appearing in the symbolic expression.
21
+ If `None`, assumes that no parameters are appearing there.
22
+ units: str, optional
23
+ The units of the variable.
24
+ Should be specified by joining atoms like `'[unit^power]'`, e.g '`[m^2][s^-2][Pa^-2]'`.
25
+ Empty by default.
26
+ latex: str, optional
27
+ Latex string representing the variable.
28
+ Empty by default.
29
+
30
+ Attributes
31
+ ----------
32
+ symbolic_expression: ~sympy.core.expr.Expr
33
+ A |Sympy| expression to represent the field mathematical expression in symbolic expressions.
34
+ units: str
35
+ The units of the variable.
36
+ Should be specified by joining atoms like `'[unit^power]'`, e.g '`[m^2][s^-2][Pa^-2]'`.
37
+ latex: str, optional
38
+ Latex string representing the variable.
39
+ expression_parameters: None or list(~parameter.Parameter), optional
40
+ List of parameters appearing in the symbolic expression.
41
+ If `None`, assumes that no parameters are appearing there.
42
+
43
+ """
44
+
45
+ def __init__(self, symbolic_expression, expression_parameters=None, units="", latex=None):
46
+
47
+ self.symbolic_expression = symbolic_expression
48
+ self.expression_parameters = expression_parameters
49
+ self.units = units
50
+ self.latex = latex
51
+
52
+ @property
53
+ def symbol(self):
54
+ """~sympy.core.expr.Expr: Synonym for the symbolic expression, to be accepted as prefactor."""
55
+ return self.symbolic_expression
56
+ @property
57
+ def numerical_expression(self):
58
+ """~sympy.core.expr.Expr: The numeric expression, i.e. with parameters replaced by their numerical value."""
59
+ substitutions = list()
60
+ if self.expression_parameters is None:
61
+ expr_parameters = list()
62
+ else:
63
+ expr_parameters = self.expression_parameters
64
+ for param in expr_parameters:
65
+ substitutions.append((param.symbol, float(param)))
66
+ return self.symbolic_expression.subs(substitutions)
67
+
68
+ def __str__(self):
69
+ return self.symbolic_expression
70
+
71
+ def __repr__(self):
72
+ return self.__str__()
73
+