zoomy-core 0.1.1__tar.gz → 0.1.2__tar.gz

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.

Files changed (61) hide show
  1. {zoomy_core-0.1.1 → zoomy_core-0.1.2}/PKG-INFO +1 -1
  2. zoomy_core-0.1.2/library/zoomy_core/decorators/decorators.py +25 -0
  3. zoomy_core-0.1.2/library/zoomy_core/fvm/flux.py +97 -0
  4. zoomy_core-0.1.2/library/zoomy_core/fvm/nonconservative_flux.py +97 -0
  5. zoomy_core-0.1.2/library/zoomy_core/fvm/ode.py +55 -0
  6. zoomy_core-0.1.2/library/zoomy_core/fvm/solver_numpy.py +305 -0
  7. zoomy_core-0.1.2/library/zoomy_core/fvm/timestepping.py +13 -0
  8. zoomy_core-0.1.2/library/zoomy_core/mesh/gmsh_loader.py +301 -0
  9. zoomy_core-0.1.2/library/zoomy_core/mesh/mesh.py +1192 -0
  10. zoomy_core-0.1.2/library/zoomy_core/mesh/mesh_extrude.py +168 -0
  11. zoomy_core-0.1.2/library/zoomy_core/mesh/mesh_util.py +487 -0
  12. zoomy_core-0.1.2/library/zoomy_core/misc/custom_types.py +6 -0
  13. zoomy_core-0.1.2/library/zoomy_core/misc/gui.py +61 -0
  14. zoomy_core-0.1.2/library/zoomy_core/misc/interpolation.py +140 -0
  15. zoomy_core-0.1.2/library/zoomy_core/misc/io.py +401 -0
  16. zoomy_core-0.1.2/library/zoomy_core/misc/logger_config.py +18 -0
  17. zoomy_core-0.1.2/library/zoomy_core/misc/misc.py +216 -0
  18. zoomy_core-0.1.2/library/zoomy_core/misc/static_class.py +94 -0
  19. zoomy_core-0.1.2/library/zoomy_core/model/analysis.py +147 -0
  20. zoomy_core-0.1.2/library/zoomy_core/model/basefunction.py +113 -0
  21. zoomy_core-0.1.2/library/zoomy_core/model/basemodel.py +512 -0
  22. zoomy_core-0.1.2/library/zoomy_core/model/boundary_conditions.py +193 -0
  23. zoomy_core-0.1.2/library/zoomy_core/model/initial_conditions.py +171 -0
  24. zoomy_core-0.1.2/library/zoomy_core/model/model.py +63 -0
  25. zoomy_core-0.1.2/library/zoomy_core/model/models/GN.py +70 -0
  26. zoomy_core-0.1.2/library/zoomy_core/model/models/advection.py +53 -0
  27. zoomy_core-0.1.2/library/zoomy_core/model/models/basisfunctions.py +181 -0
  28. zoomy_core-0.1.2/library/zoomy_core/model/models/basismatrices.py +377 -0
  29. zoomy_core-0.1.2/library/zoomy_core/model/models/core.py +564 -0
  30. zoomy_core-0.1.2/library/zoomy_core/model/models/coupled_constrained.py +60 -0
  31. zoomy_core-0.1.2/library/zoomy_core/model/models/old_smm copy.py +867 -0
  32. zoomy_core-0.1.2/library/zoomy_core/model/models/poisson.py +41 -0
  33. zoomy_core-0.1.2/library/zoomy_core/model/models/shallow_moments.py +757 -0
  34. zoomy_core-0.1.2/library/zoomy_core/model/models/shallow_moments_sediment.py +378 -0
  35. zoomy_core-0.1.2/library/zoomy_core/model/models/shallow_moments_topo.py +423 -0
  36. zoomy_core-0.1.2/library/zoomy_core/model/models/shallow_moments_variants.py +1509 -0
  37. zoomy_core-0.1.2/library/zoomy_core/model/models/shallow_water.py +266 -0
  38. zoomy_core-0.1.2/library/zoomy_core/model/models/shallow_water_topo.py +111 -0
  39. zoomy_core-0.1.2/library/zoomy_core/model/models/shear_shallow_flow.py +594 -0
  40. zoomy_core-0.1.2/library/zoomy_core/model/models/sme_turbulent.py +613 -0
  41. zoomy_core-0.1.2/library/zoomy_core/model/models/swe_old.py +1018 -0
  42. zoomy_core-0.1.2/library/zoomy_core/model/models/vam.py +455 -0
  43. zoomy_core-0.1.2/library/zoomy_core/postprocessing/postprocessing.py +72 -0
  44. zoomy_core-0.1.2/library/zoomy_core/preprocessing/openfoam_moments.py +452 -0
  45. zoomy_core-0.1.2/library/zoomy_core/transformation/helpers.py +25 -0
  46. zoomy_core-0.1.2/library/zoomy_core/transformation/to_amrex.py +238 -0
  47. zoomy_core-0.1.2/library/zoomy_core/transformation/to_c.py +181 -0
  48. zoomy_core-0.1.2/library/zoomy_core/transformation/to_jax.py +14 -0
  49. zoomy_core-0.1.2/library/zoomy_core/transformation/to_numpy.py +115 -0
  50. zoomy_core-0.1.2/library/zoomy_core/transformation/to_openfoam.py +254 -0
  51. zoomy_core-0.1.2/library/zoomy_core/transformation/to_ufl.py +67 -0
  52. {zoomy_core-0.1.1 → zoomy_core-0.1.2}/library/zoomy_core.egg-info/PKG-INFO +1 -1
  53. zoomy_core-0.1.2/library/zoomy_core.egg-info/SOURCES.txt +58 -0
  54. {zoomy_core-0.1.1 → zoomy_core-0.1.2}/pyproject.toml +2 -2
  55. zoomy_core-0.1.1/library/zoomy_core.egg-info/SOURCES.txt +0 -8
  56. {zoomy_core-0.1.1 → zoomy_core-0.1.2}/LICENSE +0 -0
  57. {zoomy_core-0.1.1 → zoomy_core-0.1.2}/README.md +0 -0
  58. {zoomy_core-0.1.1 → zoomy_core-0.1.2}/library/zoomy_core.egg-info/dependency_links.txt +0 -0
  59. {zoomy_core-0.1.1 → zoomy_core-0.1.2}/library/zoomy_core.egg-info/requires.txt +0 -0
  60. {zoomy_core-0.1.1 → zoomy_core-0.1.2}/library/zoomy_core.egg-info/top_level.txt +0 -0
  61. {zoomy_core-0.1.1 → zoomy_core-0.1.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: zoomy_core
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: A simulation software for dimensionally-reduced free surface flows.
5
5
  Author-email: Ingo Steldermann <steldermann@mbd.rwth-aachen.de>
6
6
  License: GNU
@@ -0,0 +1,25 @@
1
+
2
+ def require(requirement):
3
+ """
4
+ Decorator to check if a requirement is met before executing the decorated function.
5
+
6
+ Parameters:
7
+ - requirement (str): The requirement string to evaluate. Should evaluate to True or False.
8
+
9
+ Returns:
10
+ - wrapper: The decorated function that will check the requirement before executing.
11
+ """
12
+
13
+ # decorator to check the assertion given in requirements given the settings
14
+ def req_decorator(func):
15
+ @wraps(func)
16
+ def wrapper(settings, *args, **kwargs):
17
+ requirement_evaluated = eval(requirement)
18
+ if not requirement_evaluated:
19
+ print("Requirement {}: {}".format(requirement, requirement_evaluated))
20
+ assert requirement_evaluated
21
+ return func(settings, *args, **kwargs)
22
+
23
+ return wrapper
24
+
25
+ return req_decorator
@@ -0,0 +1,97 @@
1
+ import numpy as np
2
+
3
+ """
4
+ Dummy flux
5
+ """
6
+
7
+
8
+ def Zero():
9
+ def flux(Qi, Qj, Qauxi, Qauxj, param, normal, model_functions, mesh_props=None):
10
+ Qout = np.zeros_like(Qi)
11
+ return Qout, False
12
+
13
+ return flux
14
+
15
+
16
+ """
17
+ Lax-Friedrichs flux implementation
18
+ """
19
+
20
+
21
+ def LF():
22
+ def flux(Qi, Qj, Qauxi, Qauxj, param, normal, model_functions, mesh_props=None):
23
+ assert mesh_props is not None
24
+ dt_dx = mesh_props.dt_dx
25
+ Qout = np.zeros_like(Qi)
26
+ flux = model_functions.flux
27
+ dim = normal.shape[0]
28
+ num_eq = Qi.shape[0]
29
+ for d in range(dim):
30
+ Fi = flux[d](Qi, Qauxi, param)
31
+ Fj = flux[d](Qj, Qauxj, param)
32
+ Qout += 0.5 * (Fi + Fj) * normal[d]
33
+ Qout -= 0.5 * dt_dx * (Qj - Qi)
34
+ return Qout, False
35
+
36
+ return flux
37
+
38
+
39
+ """
40
+ Rusanov (local Lax-Friedrichs) flux implementation
41
+ """
42
+
43
+
44
+ def LLF():
45
+ def flux(Qi, Qj, Qauxi, Qauxj, param, normal, model_functions, mesh_props=None):
46
+ EVi = model_functions.eigenvalues(Qi, Qauxi, param, normal)
47
+ EVj = model_functions.eigenvalues(Qj, Qauxj, param, normal)
48
+ assert not np.isnan(EVi).any()
49
+ assert not np.isnan(EVj).any()
50
+ smax = np.max(np.abs(np.vstack([EVi, EVj])))
51
+ Qout = np.zeros_like(Qi)
52
+ flux = model_functions.flux
53
+ dim = normal.shape[0]
54
+ num_eq = Qi.shape[0]
55
+ for d in range(dim):
56
+ Fi = flux[d](Qi, Qauxi, param)
57
+ Fj = flux[d](Qj, Qauxj, param)
58
+ Qout += 0.5 * (Fi + Fj) * normal[d]
59
+ Qout -= 0.5 * smax * (Qj - Qi)
60
+ return Qout, False
61
+
62
+ return flux
63
+
64
+
65
+ """
66
+ Rusanov (local Lax-Friedrichs) flux implementation
67
+ with topography fix (e.g. for model SWEtopo)
68
+ with WB fix for lake at rest
69
+ """
70
+
71
+
72
+ def LLF_wb():
73
+ def flux(Qi, Qj, Qauxi, Qauxj, param, normal, model_functions, mesh_props=None):
74
+ IWB = np.eye(Qi.shape[0])
75
+ IWB[-1, :] = 0.0
76
+ IWB[0, -1] = 0.0
77
+ EVi = np.zeros_like(Qi)
78
+ EVj = np.zeros_like(Qj)
79
+ EVi = model_functions.eigenvalues(Qi, Qauxi, param, normal)
80
+ EVj = model_functions.eigenvalues(Qj, Qauxj, param, normal)
81
+ assert not np.isnan(EVi).any()
82
+ assert not np.isnan(EVj).any()
83
+ smax = np.max(np.abs(np.vstack([EVi, EVj])))
84
+ Qout = np.zeros_like(Qi)
85
+ flux = model_functions.flux
86
+ dim = normal.shape[0]
87
+ num_eq = Qi.shape[0]
88
+ Fi = np.zeros((num_eq))
89
+ Fj = np.zeros((num_eq))
90
+ for d in range(dim):
91
+ flux[d](Qi, Qauxi, param, Fi)
92
+ flux[d](Qj, Qauxj, param, Fj)
93
+ Qout += 0.5 * (Fi + Fj) * normal[d]
94
+ Qout -= 0.5 * smax * IWB @ (Qj - Qi)
95
+ return Qout, False
96
+
97
+ return flux
@@ -0,0 +1,97 @@
1
+ import numpy as np
2
+ from numpy.polynomial.legendre import leggauss
3
+ from functools import partial
4
+
5
+
6
+ def zero():
7
+ def nc_flux(Qi, Qauxi, Qj, Qauxj, parameters, normal, model):
8
+ return np.zeros_like(Qi), False
9
+
10
+ return nc_flux
11
+
12
+ def segmentpath(integration_order=3, scheme='rusanov'):
13
+ # compute integral of NC-Matrix int NC(Q(s)) ds for segment path Q(s) = Ql + (Qr-Ql)*s for s = [0,1]
14
+ samples, weights = leggauss(integration_order)
15
+ # shift from [-1, 1] to [0,1]
16
+ samples = 0.5 * (samples + 1)
17
+ weights *= 0.5
18
+
19
+ def priceC(
20
+ Qi, Qj, Qauxi, Qauxj, parameters, normal, svA, svB, vol_face, dt, model
21
+ ):
22
+ dim = normal.shape[0]
23
+ n_variables = Qi.shape[0]
24
+ n_cells = Qi.shape[1]
25
+
26
+ def B(s):
27
+ out = np.zeros((n_variables, n_variables, n_cells), dtype=float)
28
+ tmp = np.zeros_like(out)
29
+ for d in range(dim):
30
+ tmp = model.quasilinear_matrix[d](
31
+ Qi + s * (Qj - Qi), Qauxi + s * (Qauxj - Qauxi), parameters
32
+ )
33
+ out = out + tmp * normal[d]
34
+ # out[:,:,:] += tmp * normal[d]
35
+ return out
36
+
37
+ Bint = np.zeros((n_variables, n_variables, n_cells))
38
+ for w, s in zip(weights, samples):
39
+ Bint += w * B(s)
40
+
41
+ Bint_sq = np.einsum("ij..., jk...->ik...", Bint, Bint)
42
+ I = np.zeros_like(Bint)
43
+ for i in range(n_variables):
44
+ # I[i, i, :] = 1.
45
+ I = I.at[i, i, :].set(1.0)
46
+
47
+ Am = (
48
+ 0.5 * Bint
49
+ - (svA * svB) / (svA + svB) * 1.0 / (dt * vol_face) * I
50
+ - 1 / 4 * (dt * vol_face) / (svA + svB) * Bint_sq
51
+ )
52
+ # Am = 0.5* Bint - np.einsum('..., ij...->ij...', (svA * svB)/(svA + svB) * 1./(dt * vol_face) ,I) - 1/4 * np.einsum('..., ij...->ij...', (dt * vol_face)/(svA + svB) , Bint_sq)
53
+
54
+ return np.einsum("ij..., j...->i...", Am, (Qj - Qi)), False
55
+
56
+
57
+ def rusanov(
58
+ Qi, Qj, Qauxi, Qauxj, parameters, normal, svA, svB, vol_face, dt, model
59
+ ):
60
+ dim = normal.shape[0]
61
+ n_variables = Qi.shape[0]
62
+ n_cells = Qi.shape[1]
63
+
64
+ def B(s):
65
+ # out = np.zeros((n_variables, n_variables, n_cells), dtype=float)
66
+ # tmp = np.zeros_like(out)
67
+ # for d in range(dim):
68
+ # tmp = model.quasilinear_matrix[d](
69
+ # Qi + s * (Qj - Qi), Qauxi + s * (Qauxj - Qauxi), parameters
70
+ # )
71
+ # out = out + tmp * normal[d]
72
+ out = np.einsum("ijd..., d...->ij...", model.quasilinear_matrix(
73
+ Qi + s * (Qj - Qi), Qauxi + s * (Qauxj - Qauxi), parameters
74
+ ), normal)
75
+ return out
76
+
77
+ Am = np.zeros((n_variables, n_variables, n_cells))
78
+ for w, s in zip(weights, samples):
79
+ Am += w * B(s)
80
+
81
+ I = np.zeros_like(Am)
82
+ for i in range(n_variables):
83
+ I[i, i, :] = 1.0
84
+
85
+ ev_i = model.eigenvalues(Qi, Qauxi, parameters, normal)
86
+ ev_j = model.eigenvalues(Qj, Qauxj, parameters, normal)
87
+ sM = np.maximum(np.abs(ev_i).max(axis=0), np.abs(ev_j).max(axis=0))
88
+
89
+ return np.einsum("ij..., j...->i...", 0.5 * Am + 0.5 * sM * I, (Qj - Qi)), np.einsum("ij..., j...->i...", 0.5 * Am - 0.5 * sM * I, (Qj - Qi)), False
90
+
91
+ if scheme == 'rusanov':
92
+ return rusanov
93
+ elif scheme=='priceC':
94
+ return priceC
95
+
96
+
97
+
@@ -0,0 +1,55 @@
1
+ import numpy as np
2
+
3
+
4
+ def RK1(func, Q, Qaux, param, dt, func_jac=None, func_bc=None):
5
+ dQ = np.zeros_like(Q)
6
+ dQ = func(dt, Q, Qaux, param, dQ)
7
+ return Q + dt * dQ
8
+
9
+
10
+ def RK2(func, Q, Qaux, param, dt, func_jac=None, func_bc=None):
11
+ """
12
+ heun scheme
13
+ """
14
+ dQ = np.zeros_like(Q)
15
+ Q0 = np.array(Q)
16
+ dQ = func(dt, Q, Qaux, param, dQ)
17
+ Q1 = Q + dt * dQ
18
+ dQ = func(dt, Q1, Qaux, param, dQ)
19
+ Q2 = Q1 + dt * dQ
20
+ return 0.5 * (Q0 + Q2)
21
+
22
+
23
+ def RK3(func, Q, Qaux, param, dt, func_jac=None, func_bc=None):
24
+ """ """
25
+ dQ = np.zeros_like(Q)
26
+ Q0 = np.array(Q)
27
+ dQ = func(dt, Q, Qaux, param, dQ)
28
+ Q1 = Q + dt * dQ
29
+ dQ = func(dt, Q1, Qaux, param, dQ)
30
+ Q2 = 3.0 / 4 * Q0 + 1.0 / 4 * (Q1 + dt * dQ)
31
+ dQ = func(dt, Q2, Qaux, param, dQ)
32
+ Q3 = 1.0 / 3 * Q0 + 2 / 3 * (Q2 + dt * dQ)
33
+ # TODO see old implementation
34
+ # func(dt, Q3, Qaux, param, dQ)
35
+ return Q3
36
+
37
+
38
+ def RKimplicit(func, Q, Qaux, param, dt, func_jac=None, func_bc=None):
39
+ """
40
+ implicit euler
41
+ """
42
+ assert func_jac is not None
43
+ Jac = np.zeros((Q.shape[0], Q.shape[0], Q.shape[1]), dtype=float)
44
+ dQ = np.zeros_like(Q)
45
+ I = np.eye(Q.shape[0])
46
+
47
+ dQ = func(dt, Q, Qaux, param, dQ)
48
+ Jac = func_jac(dt, Q, Qaux, param, Jac)
49
+
50
+ b = Q + dt * dQ
51
+ for i in range(Q.shape[1]):
52
+ A = I - dt * Jac[:, :, i]
53
+ b[:, i] += -dt * np.dot(Jac[:, :, i], Q[:, i])
54
+ Q[:, i] = np.linalg.solve(A, b[:, i])
55
+ return Q
@@ -0,0 +1,305 @@
1
+ import os
2
+ from time import time as gettime
3
+
4
+ import numpy as np
5
+ from attr import define
6
+
7
+ from typing import Callable
8
+ from attrs import define, field
9
+
10
+ from library.zoomy_core.misc.logger_config import logger
11
+
12
+
13
+
14
+ import library.zoomy_core.fvm.flux as flux
15
+ import library.zoomy_core.fvm.nonconservative_flux as nonconservative_flux
16
+ import library.zoomy_core.misc.io as io
17
+ from library.zoomy_core.misc.misc import Zstruct, Settings
18
+ import library.zoomy_core.fvm.ode as ode
19
+ import library.zoomy_core.fvm.timestepping as timestepping
20
+ from library.zoomy_core.transformation.to_numpy import NumpyRuntimeModel
21
+
22
+
23
+ @define(frozen=True, slots=True, kw_only=True)
24
+ class Solver():
25
+ settings: Zstruct = field(factory=lambda: Settings.default())
26
+
27
+ def __attrs_post_init__(self):
28
+ defaults = Settings.default()
29
+ defaults.update(self.settings)
30
+ object.__setattr__(self, 'settings', defaults)
31
+
32
+
33
+ def initialize(self, mesh, model):
34
+ # model.boundary_conditions.initialize(
35
+ # mesh,
36
+ # model.time,
37
+ # model.position,
38
+ # model.distance,
39
+ # model.variables,
40
+ # model.aux_variables,
41
+ # model.parameters,
42
+ # model.normal,
43
+ # )
44
+
45
+ n_variables = model.n_variables
46
+ n_cells = mesh.n_cells
47
+ n_aux_variables = model.aux_variables.length()
48
+
49
+ Q = np.empty((n_variables, n_cells), dtype=float)
50
+ Qaux = np.empty((n_aux_variables, n_cells), dtype=float)
51
+ return Q, Qaux
52
+
53
+ def create_runtime(self, Q, Qaux, mesh, model):
54
+ mesh.resolve_periodic_bcs(model.boundary_conditions)
55
+ Q, Qaux = np.asarray(Q), np.asarray(Qaux)
56
+ parameters = np.asarray(model.parameter_values)
57
+ runtime_model = NumpyRuntimeModel(model)
58
+ return Q, Qaux, parameters, mesh, runtime_model
59
+
60
+ def get_compute_source(self, mesh, model):
61
+ def compute_source(dt, Q, Qaux, parameters, dQ):
62
+ dQ = model.source(
63
+ Q[:, :],
64
+ Qaux[:, :],
65
+ parameters,
66
+ )
67
+ return dQ
68
+
69
+ return compute_source
70
+
71
+ def get_compute_source_jacobian_wrt_variables(self, mesh, model):
72
+ def compute_source(dt, Q, Qaux, parameters, dQ):
73
+ dQ = model.source_jacobian_wrt_variables(
74
+ Q[:, : mesh.n_inner_cells],
75
+ Qaux[:, : mesh.n_inner_cells],
76
+ parameters,
77
+ )
78
+ return dQ
79
+
80
+ return compute_source
81
+
82
+ def get_apply_boundary_conditions(self, mesh, model):
83
+ # runtime_bcs = tuple(model.bcs)
84
+
85
+ # def apply_boundary_conditions(time, Q, Qaux, parameters):
86
+ # for i in range(mesh.n_boundary_faces):
87
+ # i_face = mesh.boundary_face_face_indices[i]
88
+ # i_bc_func = mesh.boundary_face_function_numbers[i]
89
+ # q_cell = Q[:, mesh.boundary_face_cells[i]] # Shape: (Q_dim,)
90
+ # qaux_cell = Qaux[:, mesh.boundary_face_cells[i]]
91
+ # normal = mesh.face_normals[:, i_face]
92
+ # position = mesh.face_centers[i_face, :]
93
+ # position_ghost = mesh.cell_centers[:, mesh.boundary_face_ghosts[i]]
94
+ # distance = np.linalg.norm(position - position_ghost)
95
+ # q_ghost = runtime_bcs[i_bc_func](time, position, distance, q_cell, qaux_cell, parameters, normal)
96
+ # Q[:, mesh.boundary_face_ghosts[i]] = q_ghost
97
+ # return Q
98
+
99
+ def apply_boundary_conditions(time, Q, Qaux, parameters):
100
+ for i in range(mesh.n_boundary_faces):
101
+ i_face = mesh.boundary_face_face_indices[i]
102
+ i_bc_func = mesh.boundary_face_function_numbers[i]
103
+ q_cell = Q[:, mesh.boundary_face_cells[i]] # Shape: (Q_dim,)
104
+ qaux_cell = Qaux[:, mesh.boundary_face_cells[i]]
105
+ normal = mesh.face_normals[:, i_face]
106
+ position = mesh.face_centers[i_face, :]
107
+ position_ghost = mesh.cell_centers[:, mesh.boundary_face_ghosts[i]]
108
+ distance = np.linalg.norm(position - position_ghost)
109
+ q_ghost = model.boundary_conditions(i_bc_func, time, position, distance, q_cell, qaux_cell, parameters, normal)
110
+ Q[:, mesh.boundary_face_ghosts[i]] = q_ghost
111
+ return Q
112
+
113
+ return apply_boundary_conditions
114
+
115
+ def update_q(self, Q, Qaux, mesh, model, parameters):
116
+ """
117
+ Update variables before the solve step.
118
+ """
119
+ # This is a placeholder implementation. Replace with actual logic as needed.
120
+ return Q
121
+
122
+ def update_qaux(self, Q, Qaux, Qold, Qauxold, mesh, model, parameters, time, dt):
123
+ """
124
+ Update auxiliary variables
125
+ """
126
+ # This is a placeholder implementation. Replace with actual logic as needed.
127
+ return Qaux
128
+
129
+ def solve(self, mesh, model):
130
+ logger.error(
131
+ "Solver.solve() is not implemented. Please implement this method in the derived class."
132
+ )
133
+ raise NotImplementedError("Solver.solve() must be implemented in derived classes.")
134
+
135
+
136
+ @define(frozen=True, slots=True, kw_only=True)
137
+ class HyperbolicSolver(Solver):
138
+ settings: Zstruct = field(factory=lambda: Settings.default())
139
+ compute_dt: Callable = field(factory=lambda: timestepping.adaptive(CFL=0.45))
140
+ num_flux: Callable = field(factory=lambda: flux.Zero())
141
+ nc_flux: Callable = field(factory=lambda: nonconservative_flux.segmentpath())
142
+ time_end: float = 0.1
143
+
144
+
145
+ def __attrs_post_init__(self):
146
+ super().__attrs_post_init__()
147
+ defaults = Settings.default()
148
+ defaults.output.update(Zstruct(snapshots=10))
149
+ defaults.update(self.settings)
150
+ object.__setattr__(self, 'settings', defaults)
151
+
152
+
153
+ def initialize(self, mesh, model):
154
+ Q, Qaux = super().initialize(mesh, model)
155
+ Q = model.initial_conditions.apply(mesh.cell_centers, Q)
156
+ Qaux = model.aux_initial_conditions.apply(mesh.cell_centers, Qaux)
157
+ return Q, Qaux
158
+
159
+ def get_compute_max_abs_eigenvalue(self, mesh, model):
160
+ def compute_max_abs_eigenvalue(Q, Qaux, parameters):
161
+ max_abs_eigenvalue = -np.inf
162
+ i_cellA = mesh.face_cells[0]
163
+ i_cellB = mesh.face_cells[1]
164
+ qA = Q[:, i_cellA]
165
+ qB = Q[:, i_cellB]
166
+ qauxA = Qaux[:, i_cellA]
167
+ qauxB = Qaux[:, i_cellB]
168
+ normal = mesh.face_normals
169
+ evA = model.eigenvalues(qA, qauxA, parameters, normal)
170
+ evB = model.eigenvalues(qB, qauxB, parameters, normal)
171
+ max_abs_eigenvalue = np.maximum(np.abs(evA).max(axis=0), np.abs(evB).max(axis=0))
172
+ return max_abs_eigenvalue
173
+ return compute_max_abs_eigenvalue
174
+
175
+ def get_flux_operator(self, mesh, model):
176
+ def flux_operator(dt, Q, Qaux, parameters, dQ):
177
+ compute_num_flux = self.num_flux
178
+ compute_nc_flux = self.nc_flux
179
+ # Initialize dQ as zeros using jax.numpy
180
+ dQ = np.zeros_like(dQ)
181
+
182
+ iA = mesh.face_cells[0]
183
+ iB = mesh.face_cells[1]
184
+
185
+ qA = Q[:, iA]
186
+ qB = Q[:, iB]
187
+ qauxA = Qaux[:, iA]
188
+ qauxB = Qaux[:, iB]
189
+ normals = mesh.face_normals
190
+ face_volumes = mesh.face_volumes
191
+ cell_volumesA = mesh.cell_volumes[iA]
192
+ cell_volumesB = mesh.cell_volumes[iB]
193
+ svA = mesh.face_subvolumes[:, 0]
194
+ svB = mesh.face_subvolumes[:, 1]
195
+
196
+ Dp, Dm, failed = compute_nc_flux(
197
+ qA,
198
+ qB,
199
+ qauxA,
200
+ qauxB,
201
+ parameters,
202
+ normals,
203
+ svA,
204
+ svB,
205
+ face_volumes,
206
+ dt,
207
+ model,
208
+ )
209
+ flux_out = Dm * face_volumes / cell_volumesA
210
+ flux_in = Dp * face_volumes / cell_volumesB
211
+
212
+ dQ[:, iA]-= flux_out
213
+ dQ[:, iB]-= flux_in
214
+ return dQ
215
+ return flux_operator
216
+
217
+ def solve(self, mesh, model, write_output=True):
218
+ Q, Qaux = self.initialize(mesh, model)
219
+
220
+ Q, Qaux, parameters, mesh, model = self.create_runtime(Q, Qaux, mesh, model)
221
+
222
+ # init once with dummy values for dt
223
+ Qaux = self.update_qaux(Q, Qaux, Q, Qaux, mesh, model, parameters, 0.0, 1.0)
224
+
225
+
226
+ if write_output:
227
+ output_hdf5_path = os.path.join(
228
+ self.settings.output.directory, f"{self.settings.output.filename}.h5"
229
+ )
230
+ save_fields = io.get_save_fields(output_hdf5_path, write_all=False)
231
+ else:
232
+ def save_fields(time, time_stamp, i_snapshot, Q, Qaux):
233
+ return i_snapshot
234
+
235
+ def run(Q, Qaux, parameters, model):
236
+ iteration = 0.0
237
+ time = 0.0
238
+
239
+ i_snapshot = 0.0
240
+ dt_snapshot = self.time_end / (self.settings.output.snapshots - 1)
241
+ if write_output:
242
+ io.init_output_directory(
243
+ self.settings.output.directory, self.settings.output.clean_directory
244
+ )
245
+ mesh.write_to_hdf5(output_hdf5_path)
246
+ io.save_settings(self.settings)
247
+ i_snapshot = save_fields(time, 0.0, i_snapshot, Q, Qaux)
248
+
249
+ Qnew = Q
250
+ Qauxnew = Qaux
251
+
252
+ compute_max_abs_eigenvalue = self.get_compute_max_abs_eigenvalue(mesh, model)
253
+ flux_operator = self.get_flux_operator(mesh, model)
254
+ source_operator = self.get_compute_source(mesh, model)
255
+ boundary_operator = self.get_apply_boundary_conditions(mesh, model)
256
+ Qnew = boundary_operator(time, Qnew, Qaux, parameters)
257
+
258
+
259
+ cell_inradius_face = np.minimum(mesh.cell_inradius[mesh.face_cells[0, :]], mesh.cell_inradius[mesh.face_cells[1,:]])
260
+
261
+ while time < self.time_end:
262
+ Q = Qnew
263
+ Qaux = Qauxnew
264
+
265
+ dt = self.compute_dt(
266
+ Q, Qaux, parameters, cell_inradius_face, compute_max_abs_eigenvalue
267
+ )
268
+
269
+ Q1 = ode.RK1(flux_operator, Q, Qaux, parameters, dt)
270
+ Q2 = ode.RK1(
271
+ source_operator,
272
+ Q1,
273
+ Qaux,
274
+ parameters,
275
+ dt,
276
+ )
277
+
278
+ Q3 = boundary_operator(time, Q2, Qaux, parameters)
279
+
280
+ # Update solution and time
281
+ time += dt
282
+ iteration += 1
283
+
284
+ time_stamp = (i_snapshot) * dt_snapshot
285
+
286
+ Qnew = self.update_q(Q3, Qaux, mesh, model, parameters)
287
+ Qauxnew = self.update_qaux(Qnew, Qaux, Q, Qaux, mesh, model, parameters, time, dt)
288
+
289
+
290
+ i_snapshot = save_fields(time, time_stamp, i_snapshot, Qnew, Qauxnew)
291
+
292
+ if iteration % 10 == 0:
293
+ logger.info(
294
+ f"iteration: {int(iteration)}, time: {float(time):.6f}, "
295
+ f"dt: {float(dt):.6f}, next write at time: {float(time_stamp):.6f}"
296
+ )
297
+
298
+ return Qnew, Qaux
299
+
300
+ time_start = gettime()
301
+ Qnew, Qaux = run(Q, Qaux, parameters, model)
302
+ time = gettime() - time_start
303
+ logger.info(f"Finished simulation with in {time:.3f} seconds")
304
+ return Qnew, Qaux
305
+
@@ -0,0 +1,13 @@
1
+ def constant(dt=0.1):
2
+ def compute_dt(Q, Qaux, parameters, min_inradius, compute_max_abs_eigenvalue):
3
+ return dt
4
+
5
+ return compute_dt
6
+
7
+
8
+ def adaptive(CFL=0.9):
9
+ def compute_dt(Q, Qaux, parameters, min_inradius, compute_max_abs_eigenvalue):
10
+ ev_abs_max = compute_max_abs_eigenvalue(Q, Qaux, parameters)
11
+ return (CFL * 2 * min_inradius / ev_abs_max).min()
12
+
13
+ return compute_dt