zoomy-core 0.1.1__py3-none-any.whl → 0.1.2__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/gmsh_loader.py +301 -0
- zoomy_core/mesh/mesh.py +1192 -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/gui.py +61 -0
- zoomy_core/misc/interpolation.py +140 -0
- zoomy_core/misc/io.py +401 -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/old_smm copy.py +867 -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/swe_old.py +1018 -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.2.dist-info}/METADATA +1 -1
- zoomy_core-0.1.2.dist-info/RECORD +55 -0
- zoomy_core-0.1.2.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.2.dist-info}/WHEEL +0 -0
- {zoomy_core-0.1.1.dist-info → zoomy_core-0.1.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from attr import define
|
|
3
|
+
from typing import Callable, Optional
|
|
4
|
+
|
|
5
|
+
from library.zoomy_core.misc.custom_types import FArray
|
|
6
|
+
from library.zoomy_core.mesh.mesh import Mesh
|
|
7
|
+
import library.zoomy_core.misc.io as io
|
|
8
|
+
import library.zoomy_core.misc.interpolation as interpolate_mesh
|
|
9
|
+
|
|
10
|
+
# from library.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
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import numpy as np
|
|
3
|
+
from typing import Union, Type
|
|
4
|
+
|
|
5
|
+
from library.zoomy_core.model.basemodel import Model
|
|
6
|
+
from library.zoomy_core.model.models.advection import Advection
|
|
7
|
+
#
|
|
8
|
+
|
|
9
|
+
# from library.zoomy_core.model.models.shallow_moments_sediment import *
|
|
10
|
+
import library.zoomy_core.model.initial_conditions as IC
|
|
11
|
+
import library.zoomy_core.model.boundary_conditions as BC
|
|
12
|
+
from library.zoomy_core.mesh.fvm_mesh import Mesh
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def create_default_mesh_and_model(
|
|
16
|
+
dimension: int = 1,
|
|
17
|
+
cls: Type[Model] = Advection,
|
|
18
|
+
fields: Union[int, list] = 1,
|
|
19
|
+
aux_variables: Union[int, list] = 0,
|
|
20
|
+
parameters: Union[int, list, dict] = 0,
|
|
21
|
+
settings: dict = {},
|
|
22
|
+
):
|
|
23
|
+
main_dir = os.getenv("ZOOMY_DIR")
|
|
24
|
+
assert main_dir != ""
|
|
25
|
+
ic = IC.Constant()
|
|
26
|
+
|
|
27
|
+
bc_tags = ["left", "right", "top", "bottom"][: 2 * dimension]
|
|
28
|
+
bcs = BC.BoundaryConditions([BC.Wall(tag=tag) for tag in bc_tags])
|
|
29
|
+
if dimension == 1:
|
|
30
|
+
mesh = Mesh.create_1d((-1, 1), 10)
|
|
31
|
+
elif dimension == 2:
|
|
32
|
+
mesh = Mesh.load_mesh(
|
|
33
|
+
os.path.join(main_dir, "meshes/quad_2d/mesh_coarse.msh"),
|
|
34
|
+
"quad",
|
|
35
|
+
2,
|
|
36
|
+
bc_tags,
|
|
37
|
+
)
|
|
38
|
+
else:
|
|
39
|
+
assert False
|
|
40
|
+
model = cls(
|
|
41
|
+
dimension=dimension,
|
|
42
|
+
fields=fields,
|
|
43
|
+
aux_variables=aux_variables,
|
|
44
|
+
parameters=parameters,
|
|
45
|
+
boundary_conditions=bcs,
|
|
46
|
+
initial_conditions=ic,
|
|
47
|
+
settings=settings,
|
|
48
|
+
)
|
|
49
|
+
n_elements = mesh.n_elements
|
|
50
|
+
|
|
51
|
+
Q = np.linspace(1, fields * n_elements, fields * n_elements).reshape(
|
|
52
|
+
n_elements, fields
|
|
53
|
+
)
|
|
54
|
+
Qaux = np.zeros((Q.shape[0], model.aux_variables.length()))
|
|
55
|
+
parameters = model.parameter_values
|
|
56
|
+
num_normals = mesh.element_n_neighbors
|
|
57
|
+
normals = np.array(
|
|
58
|
+
[mesh.element_face_normals[:, i] for i in range(mesh.n_faces_per_element)]
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
model.initial_conditions.apply(Q, mesh.element_center)
|
|
62
|
+
# model.boundary_conditions.apply()
|
|
63
|
+
return mesh, model, Q, Qaux, parameters, num_normals, normals
|
|
@@ -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 library.zoomy_core.model.basemodel import (
|
|
15
|
+
register_sympy_attribute,
|
|
16
|
+
eigenvalue_dict_to_matrix,
|
|
17
|
+
)
|
|
18
|
+
from library.zoomy_core.model.basemodel import Model
|
|
19
|
+
import library.zoomy_core.model.initial_conditions as IC
|
|
20
|
+
import library.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 library.zoomy_core.model.boundary_conditions import BoundaryConditions, Extrapolation
|
|
15
|
+
from library.zoomy_core.model.initial_conditions import InitialConditions, Constant
|
|
16
|
+
from library.zoomy_core.misc.custom_types import FArray
|
|
17
|
+
|
|
18
|
+
# from library.zoomy_core.misc import vectorize # type: ignore
|
|
19
|
+
from library.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
|