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