zoomy-core 0.1.14__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 (57) hide show
  1. decorators/decorators.py +25 -0
  2. fvm/__init__.py +0 -0
  3. fvm/flux.py +52 -0
  4. fvm/nonconservative_flux.py +97 -0
  5. fvm/ode.py +55 -0
  6. fvm/solver_numpy.py +297 -0
  7. fvm/timestepping.py +13 -0
  8. mesh/__init__.py +0 -0
  9. mesh/mesh.py +1239 -0
  10. mesh/mesh_extrude.py +168 -0
  11. mesh/mesh_util.py +487 -0
  12. misc/__init__.py +0 -0
  13. misc/custom_types.py +6 -0
  14. misc/interpolation.py +140 -0
  15. misc/io.py +448 -0
  16. misc/logger_config.py +18 -0
  17. misc/misc.py +218 -0
  18. model/__init__.py +0 -0
  19. model/analysis.py +147 -0
  20. model/basefunction.py +113 -0
  21. model/basemodel.py +513 -0
  22. model/boundary_conditions.py +193 -0
  23. model/initial_conditions.py +171 -0
  24. model/model.py +65 -0
  25. model/models/GN.py +70 -0
  26. model/models/advection.py +53 -0
  27. model/models/basisfunctions.py +181 -0
  28. model/models/basismatrices.py +381 -0
  29. model/models/coupled_constrained.py +60 -0
  30. model/models/poisson.py +41 -0
  31. model/models/shallow_moments.py +757 -0
  32. model/models/shallow_moments_sediment.py +378 -0
  33. model/models/shallow_moments_topo.py +423 -0
  34. model/models/shallow_moments_variants.py +1509 -0
  35. model/models/shallow_water.py +266 -0
  36. model/models/shallow_water_topo.py +111 -0
  37. model/models/shear_shallow_flow.py +594 -0
  38. model/models/sme_turbulent.py +613 -0
  39. model/models/vam.py +455 -0
  40. postprocessing/__init__.py +0 -0
  41. postprocessing/plotting.py +244 -0
  42. postprocessing/postprocessing.py +75 -0
  43. preprocessing/__init__.py +0 -0
  44. preprocessing/openfoam_moments.py +453 -0
  45. transformation/__init__.py +0 -0
  46. transformation/helpers.py +25 -0
  47. transformation/to_amrex.py +241 -0
  48. transformation/to_c.py +185 -0
  49. transformation/to_jax.py +14 -0
  50. transformation/to_numpy.py +118 -0
  51. transformation/to_openfoam.py +258 -0
  52. transformation/to_ufl.py +67 -0
  53. zoomy_core-0.1.14.dist-info/METADATA +52 -0
  54. zoomy_core-0.1.14.dist-info/RECORD +57 -0
  55. zoomy_core-0.1.14.dist-info/WHEEL +5 -0
  56. zoomy_core-0.1.14.dist-info/licenses/LICENSE +674 -0
  57. zoomy_core-0.1.14.dist-info/top_level.txt +8 -0
@@ -0,0 +1,241 @@
1
+ import os
2
+ from sympy import MatrixSymbol, fraction, cancel, Matrix, symbols, radsimp, powsimp
3
+ import sympy as sp
4
+ from copy import deepcopy
5
+
6
+ from zoomy_core.misc.misc import Zstruct
7
+ from zoomy_core.misc import misc as misc
8
+
9
+ from zoomy_core.model.sympy2c import create_module
10
+ from zoomy_core.transformation.helpers import regularize_denominator, substitute_sympy_attributes_with_symbol_matrix
11
+
12
+ import sympy as sp
13
+ from sympy.printing.cxx import CXX11CodePrinter
14
+ import re
15
+ import textwrap
16
+
17
+
18
+ class AmrexPrinter(CXX11CodePrinter):
19
+ """
20
+ After the normal C++ printer has done its job, replace every
21
+ 'std::foo(' with 'amrex::Math::foo(' – except if foo is listed
22
+ in 'custom_map'. No other overrides are necessary.
23
+ """
24
+ def __init__(self, model, *args, **kwargs):
25
+ super().__init__(*args, **kwargs)
26
+ self.map_Q = {k: f"Q({i})" for i, k in enumerate(model.variables.values())}
27
+ self.map_Qaux = {k: f"Qaux({i})" for i, k in enumerate(model.aux_variables.values())}
28
+ self.map_param = {k: str(float(model.parameter_values[i])) for i, k in enumerate(model.parameters.values())}
29
+ self.map_normal = {k: f"normal({i})" for i, k in enumerate(model.normal.values())}
30
+ self.map_position = {k: f"X({i})" for i, k in enumerate(model.position.values())}
31
+
32
+
33
+ self._custom_map = set({})
34
+ # names that should *keep* their std:: prefix
35
+
36
+ # pre-compile regex std::something(
37
+ self._std_regex = re.compile(r'std::([A-Za-z_]\w*)')
38
+
39
+ def _print_Symbol(self, s):
40
+ for map in [self.map_Q, self.map_Qaux, self.map_param, self.map_normal, self.map_position]:
41
+ if s in map:
42
+ return map[s]
43
+ return super()._print_Symbol(s)
44
+
45
+ def _print_Pow(self, expr):
46
+ """
47
+ Print a SymPy Power.
48
+
49
+ * integer exponent -> amrex::Math::powi<EXP>(base)
50
+ * otherwise -> amrex::Math::pow (run-time exponent)
51
+ """
52
+ base, exp = expr.as_base_exp()
53
+
54
+ # integer exponent ------------------------------------------------
55
+ if exp.is_Integer:
56
+ n = int(exp)
57
+
58
+ # 0, 1 and negative exponents inlined
59
+ if n == 0:
60
+ return "1.0"
61
+ if n == 1:
62
+ return self._print(base)
63
+ if n < 0:
64
+ # negative integer: 1 / powi<-n>(base)
65
+ return (f"(1.0 / amrex::Math::powi<{abs(n)}>("
66
+ f"{self._print(base)}))")
67
+
68
+ # positive integer
69
+ return f"amrex::Math::powi<{n}>({self._print(base)})"
70
+
71
+ # non-integer exponent -------------------------------------------
72
+ return (f"std::pow("
73
+ f"{self._print(base)}, {self._print(exp)})")
74
+
75
+ # the only method we override
76
+ def doprint(self, expr, **settings):
77
+ code = super().doprint(expr, **settings)
78
+
79
+ # callback that the regex will call for every match
80
+ def _repl(match):
81
+ fname = match.group(1)
82
+ if fname in self._custom_map:
83
+ return self._custom_map[fname]
84
+ else:
85
+ return f'std::{fname}'
86
+
87
+ # apply the replacement to the whole code string
88
+ return self._std_regex.sub(_repl, code)
89
+
90
+ def convert_expression_body(self, expr, target='res'):
91
+
92
+ tmp_sym = sp.numbered_symbols('t')
93
+ temps, simplified = sp.cse(expr, symbols=tmp_sym)
94
+ lines = []
95
+ for lhs, rhs in temps:
96
+ lines.append(f"amrex::Real {self.doprint(lhs)} = {self.doprint(rhs)};")
97
+
98
+ for i in range(expr.rows):
99
+ for j in range(expr.cols):
100
+ lines.append(f"{target}({i},{j}) = {self.doprint(simplified[0][i, j])};")
101
+
102
+ body = '\n '.join(lines)
103
+ return body
104
+
105
+ def createSmallMatrix(self, rows, cols):
106
+ return f"amrex::SmallMatrix<amrex::Real,{rows},{cols}>"
107
+
108
+ def create_file_header(self, n_dof_q, n_dof_qaux, dim):
109
+ header = textwrap.dedent(f"""
110
+ #pragma once
111
+ #include <AMReX_Array4.H>
112
+ #include <AMReX_Vector.H>
113
+
114
+ class Model {{
115
+ public:
116
+ static constexpr int n_dof_q = {n_dof_q};
117
+ static constexpr int n_dof_qaux = {n_dof_qaux};
118
+ static constexpr int dimension = {dim};
119
+ """)
120
+ return header
121
+
122
+ def create_file_footer(self):
123
+ return """
124
+ };
125
+ """
126
+
127
+ def create_function(self, name, expr, n_dof_q, n_dof_qaux, target='res'):
128
+ if type(expr) is list:
129
+ dim = len(expr)
130
+ return [self.create_function(f"{name}_{dir}", expr[i], n_dof_q, n_dof_qaux) for i, dir in enumerate(['x', 'y', 'z'][:dim])]
131
+ res_shape = expr.shape
132
+ body = self.convert_expression_body((expr), target=target)
133
+ text = f"""
134
+ AMREX_GPU_HOST_DEVICE
135
+ AMREX_FORCE_INLINE
136
+ static {self.createSmallMatrix(*res_shape)}
137
+ {name} ( {self.createSmallMatrix(n_dof_q, 1)} const& Q,
138
+ {self.createSmallMatrix(n_dof_qaux, 1)} const& Qaux) noexcept
139
+ {{
140
+ auto {target} = {self.createSmallMatrix(*res_shape)}{{}};
141
+ {body}
142
+ return {target};
143
+ }}
144
+ """
145
+ return text
146
+
147
+ def create_function_normal(self, name, expr, n_dof_q, n_dof_qaux, dim, target='res'):
148
+ if type(expr) is list:
149
+ dim = len(expr)
150
+ return [self.create_function_normal(f"{name}_{dir}", expr[i], n_dof_q, n_dof_qaux, dim) for i, dir in enumerate(['x', 'y', 'z'][:dim])]
151
+ res_shape = expr.shape
152
+ body = self.convert_expression_body(expr, target=target)
153
+ text = f"""
154
+ AMREX_GPU_HOST_DEVICE
155
+ AMREX_FORCE_INLINE
156
+ static {self.createSmallMatrix(*res_shape)}
157
+ {name} ( {self.createSmallMatrix(n_dof_q, 1)} const& Q,
158
+ {self.createSmallMatrix(n_dof_qaux, 1)} const& Qaux,
159
+ {self.createSmallMatrix(dim, 1)} const& normal) noexcept
160
+ {{
161
+ auto {target} = {self.createSmallMatrix(*res_shape)}{{}};
162
+ {body}
163
+ return {target};
164
+
165
+ }}
166
+ """
167
+ return text
168
+
169
+ def create_function_interpolate(self, name, expr, n_dof_q, n_dof_qaux, target='res'):
170
+ res_shape = expr.shape
171
+ body = self.convert_expression_body(expr, target=target)
172
+ text = f"""
173
+ AMREX_GPU_HOST_DEVICE
174
+ AMREX_FORCE_INLINE
175
+ static {self.createSmallMatrix(*res_shape)}
176
+ {name} ( {self.createSmallMatrix(n_dof_q, 1)} const& Q,
177
+ {self.createSmallMatrix(n_dof_qaux, 1)} const& Qaux,
178
+ {self.createSmallMatrix(3, 1)} const& X) noexcept
179
+ {{
180
+ auto {target} = {self.createSmallMatrix(*res_shape)}{{}};
181
+ {body}
182
+ return {target};
183
+ }}
184
+
185
+ """
186
+ return text
187
+
188
+
189
+ def create_function_boundary(self, name, expr, n_dof_q, n_dof_qaux, dim, target='res'):
190
+ res_shape = expr.shape
191
+ body = self.convert_expression_body(expr, target=target)
192
+ text = f"""
193
+ AMREX_GPU_HOST_DEVICE
194
+ AMREX_FORCE_INLINE
195
+ static {self.createSmallMatrix(*res_shape)}
196
+ {name} ( {self.createSmallMatrix(n_dof_q, 1)} const& Q,
197
+ {self.createSmallMatrix(n_dof_qaux, 1)} const& Qaux,
198
+ {self.createSmallMatrix(dim, 1)} const& normal,
199
+ {self.createSmallMatrix(3, 1)} const& position,
200
+ amrex::Real const& time,
201
+ amrex::Real const& dX) noexcept
202
+ {{
203
+ auto {target} = {self.createSmallMatrix(*res_shape)}{{}};
204
+ {body}
205
+ return {target};
206
+
207
+ }}
208
+ """
209
+ return text
210
+
211
+ def create_model(self, model):
212
+ n_dof = model.n_variables
213
+ n_dof_qaux = model.n_aux_variables
214
+ dim = model.dimension
215
+ module_functions = []
216
+ module_functions += self.create_function('flux', model.flux(), n_dof, n_dof_qaux)
217
+ module_functions += self.create_function('flux_jacobian', model.flux_jacobian(), n_dof, n_dof_qaux)
218
+ module_functions += self.create_function('nonconservative_matrix', model.nonconservative_matrix(), n_dof, n_dof_qaux)
219
+ module_functions += self.create_function('quasilinear_matrix', model.quasilinear_matrix(), n_dof, n_dof_qaux)
220
+ module_functions.append(self.create_function_normal('eigenvalues', model.eigenvalues(), n_dof, n_dof_qaux, dim))
221
+ module_functions.append(self.create_function('left_eigenvectors', model.left_eigenvectors(), n_dof, n_dof_qaux))
222
+ module_functions.append(self.create_function('right_eigenvectors', model.right_eigenvectors(), n_dof, n_dof_qaux))
223
+ module_functions.append(self.create_function('source', model.source(), n_dof, n_dof_qaux))
224
+ module_functions.append(self.create_function('residual', model.residual(), n_dof, n_dof_qaux))
225
+ module_functions.append(self.create_function('source_implicit', model.source_implicit(), n_dof, n_dof_qaux))
226
+ module_functions.append(self.create_function_interpolate('project_2d_to_3d', model.project_2d_to_3d(), n_dof, n_dof_qaux))
227
+ module_functions.append(self.create_function_boundary('boundary_conditions', model.boundary_conditions.get_boundary_condition_function(model.time, model.position, model.distance, model.variables, model.aux_variables, model.parameters, model.normal), n_dof, n_dof_qaux, dim))
228
+ full = self.create_file_header(n_dof, n_dof_qaux, dim) + '\n\n' + '\n\n'.join(module_functions) + self.create_file_footer()
229
+ return full
230
+
231
+
232
+ def write_code(model, settings):
233
+ printer = AmrexPrinter(model)
234
+ expr = printer.create_model(model)
235
+ main_dir = misc.get_main_directory()
236
+
237
+ path = os.path.join(main_dir, settings.output.directory, ".amrex_interface")
238
+ os.makedirs(path, exist_ok=True)
239
+ path = os.path.join(path, "Model.h")
240
+ with open(path, 'w+') as f:
241
+ f.write(expr)
transformation/to_c.py ADDED
@@ -0,0 +1,185 @@
1
+ import os
2
+ import re
3
+ import textwrap
4
+ import sympy as sp
5
+ from sympy.printing.cxx import CXX11CodePrinter
6
+
7
+ from zoomy_core.misc import misc as misc
8
+
9
+
10
+ class CPrinter(CXX11CodePrinter):
11
+ """
12
+ Convert SymPy expressions to C code.
13
+ """
14
+
15
+ def __init__(self, model, *args, **kwargs):
16
+ super().__init__(*args, **kwargs)
17
+
18
+ self.n_dof_q = model.n_variables
19
+ self.n_dof_qaux = model.n_aux_variables
20
+
21
+ # Map variable names to Q[i], Qaux[i]
22
+ self.map_Q = {k: f"Q[{i}]" for i, k in enumerate(model.variables.values())}
23
+ self.map_Qaux = {k: f"Qaux[{i}]" for i, k in enumerate(model.aux_variables.values())}
24
+ self.map_param = {k: str(float(model.parameter_values[i])) for i, k in enumerate(model.parameters.values())}
25
+
26
+ self.map_normal = {k: f"n[{i}]" for i, k in enumerate(model.normal.values())}
27
+ self.map_position = {k: f"X[{i}]" for i, k in enumerate(model.position.values())}
28
+
29
+ self._std_regex = re.compile(r'std::([A-Za-z_]\w*)')
30
+
31
+ # --- Symbol printing --------------------------------------------------
32
+ def _print_Symbol(self, s):
33
+ for m in [self.map_Q, self.map_Qaux, self.map_param, self.map_normal, self.map_position]:
34
+ if s in m:
35
+ return m[s]
36
+ return super()._print_Symbol(s)
37
+
38
+ # --- Pow printing -----------------------------------------------------
39
+ def _print_Pow(self, expr):
40
+ base, exp = expr.as_base_exp()
41
+ if exp.is_Integer:
42
+ n = int(exp)
43
+ if n == 0:
44
+ return "1.0"
45
+ if n == 1:
46
+ return self._print(base)
47
+ if n < 0:
48
+ return f"(1.0 / std::pow({self._print(base)}, {abs(n)}))"
49
+ return f"std::pow({self._print(base)}, {n})"
50
+ return f"std::pow({self._print(base)}, {self._print(exp)})"
51
+
52
+ # --- Expression conversion --------------------------------------------
53
+ def convert_expression_body(self, expr, target='res'):
54
+ tmp_sym = sp.numbered_symbols('t')
55
+ temps, simplified = sp.cse(expr, symbols=tmp_sym)
56
+ lines = []
57
+ cols = expr.shape[1]
58
+ for lhs, rhs in temps:
59
+ lines.append(f"double {self.doprint(lhs)} = {self.doprint(rhs)};")
60
+ for i in range(expr.rows):
61
+ for j in range(expr.cols):
62
+ lines.append(f"{target}[{i * cols +j}] = {self.doprint(simplified[0][i, j])};")
63
+ return "\n ".join(lines)
64
+
65
+ # --- Header / Footer --------------------------------------------------
66
+ def create_file_header(self, n_dof_q, n_dof_qaux, dim, list_sorted_function_names):
67
+ return textwrap.dedent(f"""\
68
+ #pragma once
69
+
70
+ static const int MODEL_n_dof_q = {n_dof_q};
71
+ static const int MODEL_n_dof_qaux = {n_dof_qaux};
72
+ static const int MODEL_dimension = {dim};
73
+ static const int MODEL_n_boundary_tags = {len(list_sorted_function_names)};
74
+ static const char* MODEL_map_boundary_tag_to_function_index[] = {{ {", ".join(f'"{item}"' for item in list_sorted_function_names)} }};
75
+ """)
76
+
77
+ def create_file_footer(self):
78
+ return "\n"
79
+
80
+ # --- Function generators ---------------------------------------------
81
+ def create_function(self, name, expr, n_dof_q, n_dof_qaux, target='res'):
82
+ if isinstance(expr, list):
83
+ dim = len(expr)
84
+ return [self.create_function(f"{name}_{d}", expr[i], n_dof_q, n_dof_qaux)
85
+ for i, d in enumerate(['x', 'y', 'z'][:dim])]
86
+
87
+ rows, cols = expr.shape
88
+ body = self.convert_expression_body(expr, target)
89
+ return f"""
90
+ inline void {name}(
91
+ const double* Q,
92
+ const double* Qaux,
93
+ double* res
94
+ )
95
+ {{
96
+ {body}
97
+ }}
98
+ """
99
+
100
+ def create_function_normal(self, name, expr, n_dof_q, n_dof_qaux, dim, target='res'):
101
+ if isinstance(expr, list):
102
+ return [self.create_function_normal(f"{name}_{d}", expr[i], n_dof_q, n_dof_qaux, dim)
103
+ for i, d in enumerate(['x', 'y', 'z'][:dim])]
104
+
105
+ rows, cols = expr.shape
106
+ body = self.convert_expression_body(expr, target)
107
+ return f"""
108
+ inline void {name}(
109
+ const double* Q,
110
+ const double* Qaux,
111
+ const double* n,
112
+ double* res)
113
+ {{
114
+ {body}
115
+ }}
116
+ """
117
+
118
+ def create_function_interpolate(self, name, expr, n_dof_q, n_dof_qaux, target='res'):
119
+ rows, cols = expr.shape
120
+ body = self.convert_expression_body(expr, target)
121
+ return f"""
122
+ inline double* {name}(
123
+ const double* Q,
124
+ const double* Qaux,
125
+ const double* X,
126
+ double* res)
127
+ {{
128
+ {body}
129
+ }}
130
+ """
131
+
132
+ def create_function_boundary(self, name, expr, n_dof_q, n_dof_qaux, dim, target='res'):
133
+ rows, cols = expr.shape
134
+ body = self.convert_expression_body(expr, target)
135
+ return f"""
136
+ inline double* {name}(
137
+ const double* Q,
138
+ const double* Qaux,
139
+ const double* n,
140
+ const double* X,
141
+ const double time,
142
+ const double dX,
143
+ double* res)
144
+ {{
145
+ {body}
146
+ }}
147
+ """
148
+
149
+ # --- Full model generation ------------------------------------------
150
+ def create_model(self, model):
151
+ n_dof = model.n_variables
152
+ n_dof_qaux = model.n_aux_variables
153
+ dim = model.dimension
154
+ funcs = []
155
+ funcs += self.create_function('flux', model.flux(), n_dof, n_dof_qaux)
156
+ funcs += self.create_function('flux_jacobian', model.flux_jacobian(), n_dof, n_dof_qaux)
157
+ funcs += self.create_function('nonconservative_matrix', model.nonconservative_matrix(), n_dof, n_dof_qaux)
158
+ funcs += self.create_function('quasilinear_matrix', model.quasilinear_matrix(), n_dof, n_dof_qaux)
159
+ funcs.append(self.create_function_normal('eigenvalues', model.eigenvalues(), n_dof, n_dof_qaux, dim))
160
+ funcs.append(self.create_function('left_eigenvectors', model.left_eigenvectors(), n_dof, n_dof_qaux))
161
+ funcs.append(self.create_function('right_eigenvectors', model.right_eigenvectors(), n_dof, n_dof_qaux))
162
+ funcs.append(self.create_function('source', model.source(), n_dof, n_dof_qaux))
163
+ funcs.append(self.create_function('residual', model.residual(), n_dof, n_dof_qaux))
164
+ funcs.append(self.create_function('source_implicit', model.source_implicit(), n_dof, n_dof_qaux))
165
+ funcs.append(self.create_function_interpolate('interpolate', model.project_2d_to_3d(), n_dof, n_dof_qaux))
166
+ funcs.append(self.create_function_boundary(
167
+ 'boundary_conditions',
168
+ model.boundary_conditions.get_boundary_condition_function(
169
+ model.time, model.position, model.distance,
170
+ model.variables, model.aux_variables, model.parameters, model.normal),
171
+ n_dof, n_dof_qaux, dim))
172
+
173
+ return self.create_file_header(n_dof, n_dof_qaux, dim, model.boundary_conditions.list_sorted_function_names) + "\n".join(funcs) + self.create_file_footer()
174
+
175
+
176
+ def write_code(model, settings):
177
+ printer = CPrinter(model)
178
+ code = printer.create_model(model)
179
+ main_dir = misc.get_main_directory()
180
+
181
+ path = os.path.join(main_dir, settings.output.directory, ".c_interface")
182
+ os.makedirs(path, exist_ok=True)
183
+ file_path = os.path.join(path, "Model.H")
184
+ with open(file_path, "w+") as f:
185
+ f.write(code)
@@ -0,0 +1,14 @@
1
+ from attrs import define
2
+ try:
3
+ import jax.numpy as jnp
4
+ except ImportError:
5
+ import numpy as jnp
6
+
7
+ from zoomy_core.transformation.to_numpy import NumpyRuntimeModel
8
+
9
+
10
+
11
+ @define(kw_only=False, slots=True, frozen=True)
12
+ class JaxRuntimeModel(NumpyRuntimeModel):
13
+ module = {'ones_like': jnp.ones_like, 'zeros_like': jnp.zeros_like, 'array': jnp.array, 'squeeze': jnp.squeeze}
14
+ printer="jax"
@@ -0,0 +1,118 @@
1
+ from typing import Callable
2
+ import numpy as np
3
+ from attrs import define, field
4
+
5
+ from zoomy_core.misc.custom_types import FArray
6
+
7
+
8
+ @define(kw_only=False, slots=True, frozen=True)
9
+ class NumpyRuntimeModel:
10
+ """Runtime model generated from a symbolic Model."""
11
+
12
+ # --- User-provided ---
13
+ model: object = field()
14
+
15
+ # --- Automatically derived ---
16
+ name: str = field(init=False)
17
+ dimension: int = field(init=False)
18
+ n_variables: int = field(init=False)
19
+ n_aux_variables: int = field(init=False)
20
+ n_parameters: int = field(init=False)
21
+ parameters: FArray = field(init=False)
22
+ flux: Callable = field(init=False)
23
+ dflux: Callable = field(init=False)
24
+ source: Callable = field(init=False)
25
+ source_jacobian_wrt_variables: Callable = field(init=False)
26
+ source_jacobian_wrt_aux_variables: Callable = field(init=False)
27
+ nonconservative_matrix: Callable = field(init=False)
28
+ quasilinear_matrix: Callable = field(init=False)
29
+ eigenvalues: Callable = field(init=False)
30
+ residual: Callable = field(init=False)
31
+ project_2d_to_3d: Callable = field(init=False)
32
+ project_3d_to_2d: Callable = field(init=False)
33
+ boundary_conditions: Callable = field(default=None, init=False)
34
+
35
+ left_eigenvectors: Callable = field(default=None, init=False)
36
+ right_eigenvectors: Callable = field(default=None, init=False)
37
+
38
+ # --- Constants ---
39
+ module = {
40
+ "ones_like": np.ones_like,
41
+ "zeros_like": np.zeros_like,
42
+ "array": np.array,
43
+ "squeeze": np.squeeze,
44
+ }
45
+ printer = "numpy"
46
+
47
+
48
+ # ------------------------------------------------------------------
49
+
50
+ def __attrs_post_init__(self):
51
+ model = self.model
52
+
53
+ modules = [self.module]
54
+ if self.printer:
55
+ modules.append(self.printer)
56
+
57
+ flux=model._flux.lambdify(modules=modules)
58
+ dflux=model._dflux.lambdify(modules=modules)
59
+ nonconservative_matrix=model._nonconservative_matrix.lambdify(
60
+ modules=modules
61
+ )
62
+ quasilinear_matrix=model._quasilinear_matrix.lambdify(
63
+ modules=modules
64
+ )
65
+ eigenvalues=model._eigenvalues.lambdify(
66
+ modules=modules
67
+ )
68
+ left_eigenvectors=model._left_eigenvectors.lambdify(
69
+ modules=modules
70
+ )
71
+ right_eigenvectors=model._right_eigenvectors.lambdify(
72
+ modules=modules
73
+ )
74
+ source=model._source.lambdify(modules=modules)
75
+ source_jacobian_wrt_variables=model._source_jacobian_wrt_variables.lambdify(
76
+ modules=modules
77
+ )
78
+ source_jacobian_wrt_aux_variables=model._source_jacobian_wrt_aux_variables.lambdify(
79
+ modules=modules
80
+ )
81
+ residual=model._residual.lambdify(modules=modules)
82
+ project_2d_to_3d=model._project_2d_to_3d.lambdify(
83
+ modules=modules
84
+ )
85
+ project_3d_to_2d=model._project_3d_to_2d.lambdify(
86
+ modules=modules
87
+ )
88
+ bcs = model._boundary_conditions.lambdify(modules=modules)
89
+
90
+
91
+ # --- Assign frozen fields -------------------------------------
92
+ object.__setattr__(self, "name", model.name)
93
+ object.__setattr__(self, "dimension", model.dimension)
94
+ object.__setattr__(self, "n_variables", model.n_variables)
95
+ object.__setattr__(self, "n_aux_variables", model.n_aux_variables)
96
+ object.__setattr__(self, "n_parameters", model.n_parameters)
97
+ object.__setattr__(self, "parameters", model.parameter_values)
98
+
99
+ object.__setattr__(self, "flux", flux)
100
+ object.__setattr__(self, "dflux", dflux)
101
+ object.__setattr__(self, "source", source)
102
+ object.__setattr__(
103
+ self, "source_jacobian_wrt_variables", source_jacobian_wrt_variables
104
+ )
105
+ object.__setattr__(
106
+ self,
107
+ "source_jacobian_wrt_aux_variables",
108
+ source_jacobian_wrt_aux_variables,
109
+ )
110
+ object.__setattr__(self, "nonconservative_matrix", nonconservative_matrix)
111
+ object.__setattr__(self, "quasilinear_matrix", quasilinear_matrix)
112
+ object.__setattr__(self, "eigenvalues", eigenvalues)
113
+ object.__setattr__(self, "left_eigenvectors", left_eigenvectors)
114
+ object.__setattr__(self, "right_eigenvectors", right_eigenvectors)
115
+ object.__setattr__(self, "residual", residual)
116
+ object.__setattr__(self, "project_2d_to_3d", project_2d_to_3d)
117
+ object.__setattr__(self, "project_3d_to_2d", project_3d_to_2d)
118
+ object.__setattr__(self, "boundary_conditions", bcs)