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,171 @@
1
+ import numpy as np
2
+ from attr import define
3
+ from typing import Callable, Optional
4
+
5
+ from zoomy_core.misc.custom_types import FArray
6
+ from zoomy_core.mesh.mesh import Mesh
7
+ import zoomy_core.misc.io as io
8
+ import zoomy_core.misc.interpolation as interpolate_mesh
9
+
10
+ # from zoomy_core.mesh import Mesh2D
11
+
12
+
13
+ @define(slots=True, frozen=True)
14
+ class InitialConditions:
15
+ def apply(self, X, Q):
16
+ assert False
17
+ return Q
18
+
19
+
20
+ @define(slots=True, frozen=True)
21
+ class Constant(InitialConditions):
22
+ constants: Callable[[int], FArray] = lambda n_variables: np.array(
23
+ [1.0] + [0.0 for i in range(n_variables - 1)]
24
+ )
25
+
26
+ def apply(self, X, Q):
27
+ n_variables = Q.shape[0]
28
+ for i in range(Q.shape[1]):
29
+ Q[:, i] = self.constants(n_variables)
30
+ return Q
31
+
32
+
33
+ @define(slots=True, frozen=False)
34
+ class RP(InitialConditions):
35
+ low: Callable[[int], FArray] = lambda n_variables: np.array(
36
+ [1.0 * (i == 0) for i in range(n_variables)]
37
+ )
38
+ high: Callable[[int], FArray] = lambda n_variables: np.array(
39
+ [2.0 * (i == 0) for i in range(n_variables)]
40
+ )
41
+ jump_position_x: float = 0.0
42
+
43
+ def apply(self, X, Q):
44
+ assert X.shape[1] == Q.shape[1]
45
+ n_variables = Q.shape[0]
46
+ for i in range(Q.shape[1]):
47
+ if X[0, i] < self.jump_position_x:
48
+ Q[:, i] = self.high(n_variables)
49
+ else:
50
+ Q[:, i] = self.low(n_variables)
51
+ return Q
52
+
53
+
54
+ @define(slots=True, frozen=False)
55
+ class RP2d(InitialConditions):
56
+ low: Callable[[int], FArray] = lambda n_variables: np.array(
57
+ [1.0 * (i == 0) for i in range(n_variables)]
58
+ )
59
+ high: Callable[[int], FArray] = lambda n_variables: np.array(
60
+ [2.0 * (i == 0) for i in range(n_variables)]
61
+ )
62
+ jump_position_x: float = 0.0
63
+ jump_position_y: float = 0.0
64
+
65
+ def apply(self, X, Q):
66
+ assert X.shape[1] == Q.shape[1]
67
+ n_variables = Q.shape[0]
68
+ for i in range(Q.shape[1]):
69
+ if X[0, i] < self.jump_position_x and X[1, i] < self.jump_position_y:
70
+ Q[:, i] = self.high(n_variables)
71
+ else:
72
+ Q[:, i] = self.low(n_variables)
73
+ return Q
74
+
75
+
76
+ @define(slots=True, frozen=False)
77
+ class RP3d(InitialConditions):
78
+ low: Callable[[int], FArray] = lambda n_variables: np.array(
79
+ [1.0 * (i == 0) for i in range(n_variables)]
80
+ )
81
+ high: Callable[[int], FArray] = lambda n_variables: np.array(
82
+ [2.0 * (i == 0) for i in range(n_variables)]
83
+ )
84
+ jump_position_x: float = 0.0
85
+ jump_position_y: float = 0.0
86
+ jump_position_z: float = 0.0
87
+
88
+ def apply(self, X, Q):
89
+ assert X.shape[1] == Q.shape[1]
90
+ n_variables = Q.shape[0]
91
+ for i in range(Q.shape[1]):
92
+ if (
93
+ X[0, i] < self.jump_position_x
94
+ and X[1, i] < self.jump_position_y
95
+ and X[2, i] < self.jump_position_z
96
+ ):
97
+ Q[:, i] = self.high(n_variables)
98
+ else:
99
+ Q[:, i] = self.low(n_variables)
100
+ return Q
101
+
102
+
103
+ @define(slots=True, frozen=False)
104
+ class RadialDambreak(InitialConditions):
105
+ low: Callable[[int], FArray] = lambda n_variables: np.array(
106
+ [1.0 * (i == 0) for i in range(n_variables)]
107
+ )
108
+ high: Callable[[int], FArray] = lambda n_variables: np.array(
109
+ [2.0 * (i == 0) for i in range(n_variables)]
110
+ )
111
+ radius: float = 0.1
112
+
113
+ def apply(self, X, Q):
114
+ dim = X.shape[0]
115
+ center = np.zeros(dim)
116
+ for d in range(dim):
117
+ center[d] = X[d, :].mean()
118
+ assert X.shape[1] == Q.shape[1]
119
+ n_variables = Q.shape[0]
120
+ for i in range(Q.shape[1]):
121
+ if np.linalg.norm(X[:, i] - center) <= self.radius:
122
+ Q[:, i] = self.high(n_variables)
123
+ else:
124
+ Q[:, i] = self.low(n_variables)
125
+ return Q
126
+
127
+
128
+ @define(slots=True, frozen=True)
129
+ class UserFunction(InitialConditions):
130
+ function: Optional[Callable[[FArray], FArray]] = None
131
+
132
+ def apply(self, X, Q):
133
+ assert X.shape[1] == Q.shape[1]
134
+ if self.function is None:
135
+ self.function = lambda x: np.zeros(Q.shape[1])
136
+ for i, x in enumerate(X.T):
137
+ Q[:, i] = self.function(x)
138
+ return Q
139
+
140
+
141
+ # TODO do time interpolation
142
+ @define(slots=True, frozen=True)
143
+ class RestartFromHdf5(InitialConditions):
144
+ path_to_fields: Optional[str] = None
145
+ mesh_new: Optional[Mesh] = None
146
+ mesh_identical: bool = False
147
+ path_to_old_mesh: Optional[str] = None
148
+ snapshot: Optional[int] = -1
149
+ map_fields: Optional[dict] = None
150
+
151
+ def apply(self, X, Q):
152
+ assert self.mesh_new is not None
153
+ assert self.path_to_fields is not None
154
+ assert X.shape[0] == Q.shape[0]
155
+ if self.map_fields is None:
156
+ map_fields = {i: i for i in range(Q.shape[1])}
157
+ else:
158
+ map_fields = self.map_fields
159
+ mesh = Mesh.from_hdf5(self.path_to_old_mesh)
160
+ _Q, _Qaux, time = io.load_fields_from_hdf5(
161
+ self.path_to_fields, i_snapshot=self.snapshot
162
+ )
163
+ Q = np.zeros_like(Q)
164
+ if self.mesh_identical:
165
+ Q[:, list(map_fields.values())] = _Q[:, list(map_fields.keys())]
166
+ else:
167
+ assert self.path_to_old_mesh is not None
168
+ Q[:, list(map_fields.values())] = interpolate_mesh.to_new_mesh(
169
+ _Q, mesh, self.mesh_new
170
+ )[:, list(map_fields.keys())]
171
+ return Q
model/model.py ADDED
@@ -0,0 +1,65 @@
1
+ import os
2
+ import numpy as np
3
+ from typing import Union, Type
4
+
5
+ from zoomy_core.model.basemodel import Model
6
+ from zoomy_core.model.models.advection import Advection
7
+ #
8
+
9
+ # from zoomy_core.model.models.shallow_moments_sediment import *
10
+ import zoomy_core.model.initial_conditions as IC
11
+ import zoomy_core.model.boundary_conditions as BC
12
+ from zoomy_core.mesh.fvm_mesh import Mesh
13
+ from zoomy_core.misc import misc as misc
14
+
15
+
16
+ def create_default_mesh_and_model(
17
+ dimension: int = 1,
18
+ cls: Type[Model] = Advection,
19
+ fields: Union[int, list] = 1,
20
+ aux_variables: Union[int, list] = 0,
21
+ parameters: Union[int, list, dict] = 0,
22
+ settings: dict = {},
23
+ ):
24
+ main_dir = misc.get_main_directory()
25
+
26
+ assert main_dir != ""
27
+ ic = IC.Constant()
28
+
29
+ bc_tags = ["left", "right", "top", "bottom"][: 2 * dimension]
30
+ bcs = BC.BoundaryConditions([BC.Wall(tag=tag) for tag in bc_tags])
31
+ if dimension == 1:
32
+ mesh = Mesh.create_1d((-1, 1), 10)
33
+ elif dimension == 2:
34
+ mesh = Mesh.load_mesh(
35
+ os.path.join(main_dir, "meshes/quad_2d/mesh_coarse.msh"),
36
+ "quad",
37
+ 2,
38
+ bc_tags,
39
+ )
40
+ else:
41
+ assert False
42
+ model = cls(
43
+ dimension=dimension,
44
+ fields=fields,
45
+ aux_variables=aux_variables,
46
+ parameters=parameters,
47
+ boundary_conditions=bcs,
48
+ initial_conditions=ic,
49
+ settings=settings,
50
+ )
51
+ n_elements = mesh.n_elements
52
+
53
+ Q = np.linspace(1, fields * n_elements, fields * n_elements).reshape(
54
+ n_elements, fields
55
+ )
56
+ Qaux = np.zeros((Q.shape[0], model.aux_variables.length()))
57
+ parameters = model.parameter_values
58
+ num_normals = mesh.element_n_neighbors
59
+ normals = np.array(
60
+ [mesh.element_face_normals[:, i] for i in range(mesh.n_faces_per_element)]
61
+ )
62
+
63
+ model.initial_conditions.apply(Q, mesh.element_center)
64
+ # model.boundary_conditions.apply()
65
+ return mesh, model, Q, Qaux, parameters, num_normals, normals
model/models/GN.py ADDED
@@ -0,0 +1,70 @@
1
+ import numpy as np
2
+ import numpy.polynomial.legendre as L
3
+ import numpy.polynomial.chebyshev as C
4
+ from scipy.optimize import least_squares as lsq
5
+ import sympy
6
+ from sympy import Matrix, sqrt
7
+ from sympy.abc import x
8
+
9
+ from sympy import integrate, diff
10
+ from sympy import legendre
11
+ from sympy import lambdify, Rational
12
+
13
+
14
+ from zoomy_core.model.basemodel import (
15
+ register_sympy_attribute,
16
+ eigenvalue_dict_to_matrix,
17
+ )
18
+ from zoomy_core.model.basemodel import Model
19
+ import zoomy_core.model.initial_conditions as IC
20
+ import zoomy_core.model.boundary_conditions as BC
21
+
22
+ class GN(Model):
23
+ def __init__(
24
+ self,
25
+ boundary_conditions= None,
26
+ initial_conditions=None,
27
+ dimension=1,
28
+ fields=2,
29
+ # D = h^3 / 3 * (dt * dx * u + u * dx^2 u - (dx u)^2)
30
+ aux_variables=['dD_dx'],
31
+ parameters={},
32
+ _default_parameters={"g": 9.81},
33
+ settings={},
34
+ settings_default={},
35
+ ):
36
+ self.variables = register_sympy_attribute(fields, "q")
37
+ self.n_variables = self.variables.length()
38
+ super().__init__(
39
+ dimension=dimension,
40
+ fields=fields,
41
+ aux_variables=aux_variables,
42
+ parameters=parameters,
43
+ _default_parameters=_default_parameters,
44
+ boundary_conditions=boundary_conditions,
45
+ initial_conditions=initial_conditions,
46
+ settings={**settings_default, **settings},
47
+ )
48
+
49
+ def flux(self):
50
+ fx = Matrix([0 for i in range(self.n_variables)])
51
+ h = self.variables[0]
52
+ hu = self.variables[1]
53
+
54
+ param = self.parameters
55
+
56
+ fx[0] = hu
57
+ fx[1] = hu**2 / h + 1/2 * param.g * h**2
58
+ return [fx]
59
+
60
+
61
+
62
+ def source_implicit(self):
63
+ R = Matrix([0 for i in range(self.n_variables)])
64
+ dD_dx = self.aux_variables.dD_dx
65
+
66
+
67
+ R[0] = 0
68
+ R[1] = dD_dx
69
+ return R
70
+
@@ -0,0 +1,53 @@
1
+ import numpy as np
2
+ import os
3
+ # import logging
4
+
5
+ import sympy
6
+ from sympy import Symbol, Matrix, lambdify, transpose, Abs, sqrt
7
+
8
+ from sympy import zeros, ones
9
+
10
+ from attr import define
11
+ from typing import Optional
12
+ from types import SimpleNamespace
13
+
14
+ from zoomy_core.model.boundary_conditions import BoundaryConditions, Extrapolation
15
+ from zoomy_core.model.initial_conditions import InitialConditions, Constant
16
+ from zoomy_core.misc.custom_types import FArray
17
+
18
+ # from zoomy_core.misc import vectorize # type: ignore
19
+ from zoomy_core.model.basemodel import Model
20
+
21
+
22
+ class Advection(Model):
23
+ def flux(self):
24
+ if self.dimension == 1:
25
+ F = Matrix([0 for i in range(self.n_variables)])
26
+ for i_field in range(self.n_variables):
27
+ F[i_field] = self.variables[i_field] * self.parameters[0]
28
+ return [F]
29
+ elif self.dimension == 2:
30
+ F = Matrix([0 for i in range(self.n_variables)])
31
+ G = Matrix([0 for i in range(self.n_variables)])
32
+ for i_field in range(self.n_variables):
33
+ F[i_field] = self.variables[i_field] * self.parameters[0]
34
+ G[i_field] = self.variables[i_field] * self.parameters[1]
35
+ return [F, G]
36
+ elif self.dimension == 3:
37
+ F = Matrix([0 for i in range(self.n_variables)])
38
+ G = Matrix([0 for i in range(self.n_variables)])
39
+ H = Matrix([0 for i in range(self.n_variables)])
40
+ for i_field in range(self.n_variables):
41
+ F[i_field] = self.variables[i_field] * self.parameters[0]
42
+ G[i_field] = self.variables[i_field] * self.parameters[1]
43
+ H[i_field] = self.variables[i_field] * self.parameters[2]
44
+ return [F, G, H]
45
+ else:
46
+ assert False
47
+
48
+ # def eigenvalues(self):
49
+ # assert self.normal.shape[0] == self.parameters.shape[0]
50
+ # ev = self.normal[0] * self.parameters[0]
51
+ # for d in range(1, self.dimension):
52
+ # ev += self.normal[d] * self.parameters[d]
53
+ # self.sympy_eigenvalues = Matrix[[ev for i in range(self.n_variables)]]
@@ -0,0 +1,181 @@
1
+ from copy import deepcopy
2
+
3
+ import numpy as np
4
+ import sympy
5
+ from sympy import Symbol, bspline_basis_set, diff, integrate, lambdify, legendre
6
+ from sympy.abc import z
7
+ from sympy.functions.special.polynomials import chebyshevu
8
+
9
+
10
+ class Basisfunction:
11
+ name = "Basisfunction"
12
+
13
+ def bounds(self):
14
+ return [0, 1]
15
+
16
+ def basis_definition(self):
17
+ z = Symbol("z")
18
+ b = lambda k, z: z**k
19
+ return [b(k, z) for k in range(self.level + 1)]
20
+
21
+ def weight(self, z):
22
+ return 1
23
+
24
+ def weight_eval(self, z):
25
+ z = Symbol("z")
26
+ f = sympy.lambdify(z, self.weight(z))
27
+ return f(z)
28
+
29
+ def __init__(self, level=0, **kwargs):
30
+ self.level = level
31
+ self.basis = self.basis_definition(**kwargs)
32
+
33
+ def get(self, k):
34
+ return self.basis[k]
35
+
36
+ def eval(self, k, _z):
37
+ return self.get(k).subs(z, _z)
38
+
39
+ def eval_psi(self, k, _z):
40
+ z = sympy.Symbol('z')
41
+ psi = sympy.integrate(self.get(k), (z, self.bounds()[0], z))
42
+ return psi.subs(z, _z)
43
+
44
+ def get_lambda(self, k):
45
+ f = lambdify(z, self.get(k))
46
+
47
+ def lam(z):
48
+ if type(z) == int or type(z) == float:
49
+ return f(z)
50
+ elif type(z) == list or type(z) == np.ndarray:
51
+ return np.array([f(xi) for xi in z])
52
+ else:
53
+ assert False
54
+
55
+ return lam
56
+
57
+ def plot(self, ax):
58
+ X = np.linspace(self.bounds()[0], self.bounds()[1], 1000)
59
+ for i in range(len(self.basis)):
60
+ f = lambdify(z, self.get(i))
61
+ y = np.array([f(xi) for xi in X])
62
+ ax.plot(X, y, label=f"basis {i}")
63
+
64
+ def reconstruct_velocity_profile(self, alpha, N=100):
65
+ Z = np.linspace(self.bounds()[0], self.bounds()[1], N)
66
+ u = np.zeros_like(Z)
67
+ for i in range(len(self.basis)):
68
+ b = lambdify(z, self.get(i))
69
+ u[:] += alpha[i] * b(Z)
70
+ return u
71
+
72
+ def reconstruct_velocity_profile_at(self, alpha, z):
73
+ u = 0
74
+ for i in range(len(self.basis)):
75
+ b = lambdify(z, self.eval(i, z))
76
+ u += alpha[i] * b(z)
77
+ return u
78
+
79
+ def reconstruct_alpha(self, velocities, z):
80
+ n_basis = len(self.basis)
81
+ alpha = np.zeros(n_basis)
82
+ for i in range(n_basis):
83
+ b = lambdify(z, self.eval(i, z))
84
+ nom = np.trapz(velocities * b(z) * self.weight(z), z)
85
+ if type(b(z)) == int:
86
+ den = b(z) ** 2
87
+ else:
88
+ den = np.trapz((b(z) * b(z)).reshape(z.shape), z)
89
+ res = nom / den
90
+ alpha[i] = res
91
+ return alpha
92
+
93
+ def project_onto_basis(self, Y):
94
+ Z = np.linspace(self.bounds()[0], self.bounds()[1], Y.shape[0])
95
+ n_basis = len(self.basis)
96
+ alpha = np.zeros(n_basis)
97
+ z = Symbol("z")
98
+ for i in range(n_basis):
99
+ b = lambdify(z, self.eval(i, Z))
100
+ alpha[i] = np.trapz(Y * b(Z) * self.weight_eval(Z), Z)
101
+ return alpha
102
+
103
+ def get_diff_basis(self):
104
+ db = [diff(b, z) for i, b in enumerate(self.basis)]
105
+ return db
106
+
107
+
108
+ class Monomials(Basisfunction):
109
+ name = "Monomials"
110
+
111
+
112
+ class Legendre_shifted(Basisfunction):
113
+ name = "Legendre_shifted"
114
+
115
+ def basis_definition(self):
116
+ z = Symbol("z")
117
+ b = lambda k, z: legendre(k, 2 * z - 1) * (-1) ** (k)
118
+ return [b(k, z) for k in range(self.level + 1)]
119
+
120
+ class Chebyshevu(Basisfunction):
121
+ name = "Chebyshevu"
122
+
123
+ def bounds(self):
124
+ return [-1, 1]
125
+
126
+ def weight(self, z):
127
+ # do not forget to include the jacobian of the coordinate transformation in the weight
128
+ return sympy.sqrt(1-z**2)
129
+
130
+ def basis_definition(self):
131
+ z = Symbol("z")
132
+ b = lambda k, z: sympy.sqrt(2 / sympy.pi) * chebyshevu(k, z)
133
+ return [b(k, z) for k in range(self.level + 1)]
134
+
135
+ class Legendre_DN(Basisfunction):
136
+ name = "Legendre_DN - satifying no-slip and no-stress. This is a non-SWE basis"
137
+
138
+ def bounds(self):
139
+ return [-1, 1]
140
+
141
+ def basis_definition(self):
142
+ z = Symbol("z")
143
+ def b(k, z):
144
+ alpha = sympy.Rational((2*k+3), (k+2)**2)
145
+ beta = -sympy.Rational((k+1),(k+2))**2
146
+ return (legendre(k, z) ) + alpha * (legendre(k+1, z) ) + beta * (legendre(k+2, z))
147
+ #normalizing makes no sence, as b(k, 0) = 0 by construction
148
+ return [b(k, z) for k in range(self.level + 1)]
149
+
150
+
151
+ class Spline(Basisfunction):
152
+ name = "Spline"
153
+
154
+ def basis_definition(self, degree=1, knots=[0, 0, 0.001, 1, 1]):
155
+ z = Symbol("z")
156
+ basis = bspline_basis_set(degree, knots, z)
157
+ return basis
158
+
159
+
160
+ class OrthogonalSplineWithConstant(Basisfunction):
161
+ name = "OrthogonalSplineWithConstant"
162
+
163
+ def basis_definition(self, degree=1, knots=[0, 0, 0.5, 1, 1]):
164
+ z = Symbol("z")
165
+
166
+ def prod(u, v):
167
+ return integrate(u * v, (z, 0, 1))
168
+
169
+ basis = bspline_basis_set(degree, knots, z)
170
+ add_basis = [1]
171
+ # add_basis = [sympy.Piecewise((0, z<0.1), (1, True))]
172
+ basis = add_basis + basis[:-1]
173
+ orth = deepcopy(basis)
174
+ for i in range(1, len(orth)):
175
+ for j in range(0, i):
176
+ orth[i] -= prod(basis[i], orth[j]) / prod(orth[j], orth[j]) * orth[j]
177
+
178
+ for i in range(len(orth)):
179
+ orth[i] /= sympy.sqrt(prod(orth[i], orth[i]))
180
+
181
+ return orth