Skip to content

Commit 151a9f6

Browse files
author
arthur.loison
committed
two-scale model
1 parent 7a5f7f7 commit 151a9f6

14 files changed

+2033
-267
lines changed

josie/tsfoureq/exact.py

+250-115
Large diffs are not rendered by default.

josie/tsfoureq/problem.py

+15-5
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,19 @@ def F(self, values: Q) -> np.ndarray:
3838

3939
arho1 = values[..., fields.arho1]
4040
arho2 = values[..., fields.arho2]
41-
arho = values[..., fields.arho]
42-
rho = arho1 + arho2
41+
arho1d = values[..., fields.arho1d]
42+
abarrho = values[..., fields.abarrho]
43+
rho = arho1 + arho2 + arho1d
44+
ad = values[..., fields.ad]
4345
rhoU = values[..., fields.rhoU]
4446
rhoV = values[..., fields.rhoV]
4547
U = rhoU / rho
4648
V = rhoV / rho
47-
p = values[..., fields.P]
49+
p = values[..., fields.pbar]
4850

49-
F[..., TSFourEqConsFields.arho, Direction.X] = arho * U
50-
F[..., TSFourEqConsFields.arho, Direction.Y] = arho * V
51+
# Four-eq like model
52+
F[..., TSFourEqConsFields.abarrho, Direction.X] = abarrho * U
53+
F[..., TSFourEqConsFields.abarrho, Direction.Y] = abarrho * V
5154

5255
F[..., TSFourEqConsFields.arho1, Direction.X] = arho1 * U
5356
F[..., TSFourEqConsFields.arho1, Direction.Y] = arho1 * V
@@ -60,4 +63,11 @@ def F(self, values: Q) -> np.ndarray:
6063
F[..., TSFourEqConsFields.rhoV, Direction.X] = rhoV * U
6164
F[..., TSFourEqConsFields.rhoV, Direction.Y] = rhoV * V + p
6265

66+
# Small-scale variables
67+
F[..., TSFourEqConsFields.arho1d, Direction.X] = arho1d * U
68+
F[..., TSFourEqConsFields.arho1d, Direction.Y] = arho1d * V
69+
70+
F[..., TSFourEqConsFields.ad, Direction.X] = ad * U
71+
F[..., TSFourEqConsFields.ad, Direction.Y] = ad * V
72+
6373
return F

josie/tsfoureq/schemes.py

+118-124
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@ def __init__(self, eos: TwoPhaseEOS, do_relaxation: bool):
2929
def relaxForLinearizedEOS(self, values: Q):
3030
fields = Q.fields
3131

32-
arho1 = values[..., fields.arho1]
33-
arho2 = values[..., fields.arho2]
32+
ad = values[..., fields.ad]
33+
arho1 = values[..., fields.arho1] / (1 - ad)
34+
arho2 = values[..., fields.arho2] / (1 - ad)
35+
arho1d = values[..., fields.arho1d]
3436

3537
rho10 = self.problem.eos[Phases.PHASE1].rho0
3638
rho20 = self.problem.eos[Phases.PHASE2].rho0
@@ -48,8 +50,10 @@ def relaxForLinearizedEOS(self, values: Q):
4850
) / (2.0 * arho2 * c2**2)
4951

5052
alpha = betaPos / (1.0 + betaPos)
51-
values[..., fields.alpha] = alpha
52-
values[..., fields.arho] = alpha * (arho1 + arho2)
53+
values[..., fields.abar] = alpha
54+
values[..., fields.abarrho] = alpha * (
55+
arho1 * (1 - ad) + arho2 * (1 - ad) + arho1d
56+
)
5357

5458
"""General relaxation procedure for all other EOS"""
5559

@@ -58,134 +62,117 @@ def relaxation(self, values: Q):
5862

5963
arho1 = values[..., fields.arho1]
6064
arho2 = values[..., fields.arho2]
65+
arho1d = values[..., fields.arho1d]
66+
ad = values[..., fields.ad]
67+
abarrho = values[..., fields.abarrho]
68+
rho = arho1 + arho2 + arho1d
6169

6270
# Compute estimator of the relaxation within [0,1]
63-
alpha = np.minimum(np.maximum(values[..., fields.arho] / (arho1 + arho2), 0), 1)
71+
abar = np.minimum(np.maximum(abarrho / rho, 0), 1)
72+
73+
values[..., fields.abar] = abar
6474

65-
# Solve for alpha using p1(arho1/alpha) = p2(arho2/alpha) with Newton
66-
# Note that arho1 and arho2 remain constant
67-
def phi(arho1: np.ndarray, arho2: np.ndarray, alpha: np.ndarray):
75+
# Note that rho remain constant
76+
def phi(
77+
arho1: np.ndarray,
78+
arho2: np.ndarray,
79+
abar: np.ndarray,
80+
ad: np.ndarray,
81+
):
6882
rho1 = np.full_like(arho1, np.nan)
6983
rho2 = np.full_like(arho1, np.nan)
70-
np.divide(arho1, alpha, where=(alpha > 0) & (arho1 > 0), out=rho1)
84+
np.divide(arho1, abar * (1 - ad), where=(abar > 0) & (arho1 > 0), out=rho1)
7185
np.divide(
72-
arho2, 1.0 - alpha, where=(1.0 - alpha > 0) & (arho2 > 0), out=rho2
86+
arho2,
87+
(1 - abar) * (1 - ad),
88+
where=((1.0 - abar) > 0) & (arho2 > 0),
89+
out=rho2,
90+
)
91+
return (1 - ad) * (
92+
self.problem.eos[Phases.PHASE1].p(rho1)
93+
- self.problem.eos[Phases.PHASE2].p(rho2)
7394
)
74-
return self.problem.eos[Phases.PHASE1].p(rho1) - self.problem.eos[
75-
Phases.PHASE2
76-
].p(rho2)
7795

78-
def dphi_dalpha(arho1: np.ndarray, arho2: np.ndarray, alpha: np.ndarray):
96+
def dphi_dabar(
97+
arho1: np.ndarray, arho2: np.ndarray, abar: np.ndarray, ad: np.ndarray
98+
):
7999
# Note that dp_drho = c^2 for barotropic EOS
80100
return (
81101
-arho1
82-
/ (alpha**2)
83-
* self.problem.eos[Phases.PHASE1].sound_velocity(arho1 / alpha) ** 2
102+
/ (abar**2)
103+
* self.problem.eos[Phases.PHASE1].sound_velocity(
104+
arho1 / abar / (1 - ad)
105+
)
106+
** 2
84107
- arho2
85-
/ ((1.0 - alpha) ** 2)
86-
* self.problem.eos[Phases.PHASE2].sound_velocity(arho2 / (1.0 - alpha))
108+
/ ((1.0 - abar) ** 2)
109+
* self.problem.eos[Phases.PHASE2].sound_velocity(
110+
arho2 / (1.0 - abar) / (1 - ad)
111+
)
87112
** 2
88113
)
89114

90115
# Init NR method
91-
dalpha = np.zeros_like(alpha)
116+
dabar = np.zeros_like(abar)
92117
iter = 0
93118

94119
# Index that locates the cell where there the pressures need to be relaxed
95120
eps = 1e-9
96-
index = np.where(np.abs(phi(arho1, arho2, alpha)) > eps * 1e5)
121+
tol = 1e-5
122+
p0 = self.problem.eos[Phases.PHASE1].p0
123+
index = np.where(
124+
(np.abs(phi(arho1, arho2, abar, ad)) > tol * p0)
125+
& (abar > eps)
126+
& (1 - abar > eps)
127+
)
97128
while index[0].size > 0:
98129
# Counter
99130
iter += 1
100131

101132
# NR step
102-
dalpha[index] = -phi(
103-
arho1[index], arho2[index], alpha[index]
104-
) / dphi_dalpha(arho1[index], arho2[index], alpha[index])
133+
dabar[index] = -phi(arho1[index], arho2[index], abar[index], ad[index]) / (
134+
dphi_dabar(arho1[index], arho2[index], abar[index], ad[index])
135+
)
105136

106137
# Prevent the NR method to explore out of the interval [0,1]
107-
alpha[index] += np.where(
108-
dalpha[index] < 0,
109-
np.maximum(dalpha[index], -0.9 * alpha[index]),
110-
np.minimum(dalpha[index], 0.9 * (1 - alpha[index])),
138+
abar[index] += np.where(
139+
dabar[index] < 0,
140+
np.maximum(dabar[index], -0.9 * abar[index]),
141+
np.minimum(dabar[index], 0.9 * (1 - abar[index])),
111142
)
112-
tol = 1e-6
113-
alpha = np.where(alpha < tol, 0, alpha)
114-
alpha = np.where(1 - alpha < tol, 1, alpha)
115143

116144
# Update the index where the NR method is applied
117-
index = np.where((np.abs(phi(arho1, arho2, alpha)) > eps * 1e5))
145+
index = np.where(
146+
(np.abs(phi(arho1, arho2, abar, ad)) > tol * p0)
147+
& (abar > eps)
148+
& (1 - abar > eps)
149+
)
118150

119151
# Safety check
120152
if iter > 50:
121153
exit()
122154

123-
# Update the alpha-dependent conservative field
124-
values[..., fields.arho] = alpha * (arho1 + arho2)
125-
126-
def prim2Q(self, values: Q):
127-
fields = Q.fields
128-
129-
rho = values[..., fields.rho]
130-
P = values[..., fields.P]
131-
U = values[..., fields.U]
132-
V = values[..., fields.V]
133-
134-
rho1 = self.problem.eos[Phases.PHASE1].rho(P)
135-
rho2 = self.problem.eos[Phases.PHASE2].rho(P)
136-
c1 = self.problem.eos[Phases.PHASE1].sound_velocity(rho1)
137-
c2 = self.problem.eos[Phases.PHASE2].sound_velocity(rho2)
138-
139-
alpha = np.minimum(np.maximum((rho - rho2) / (rho1 - rho2), 0), 1)
140-
# alpha = (rho - rho2) / (rho1 - rho2)
141-
142-
if np.any(alpha < 0.0) or np.any(alpha > 1.0):
143-
print(alpha)
144-
exit()
145-
if np.any(rho < 0):
146-
print(rho)
147-
exit()
148-
149-
values[..., fields.arho] = alpha * rho
150-
values[..., fields.arho1] = alpha * rho1
151-
values[..., fields.arho2] = (1 - alpha) * rho2
152-
values[..., fields.rhoU] = rho * U
153-
values[..., fields.rhoV] = rho * V
154-
155-
values[..., fields.c] = np.sqrt(
156-
(alpha * rho1 * c1**2 + (1 - alpha) * rho2 * c2**2) / rho
157-
)
158-
values[..., fields.alpha] = alpha
159-
values[..., fields.p1] = P
160-
values[..., fields.p2] = P
161-
values[..., fields.c1] = c1
162-
values[..., fields.c2] = c2
155+
# Update the abar-dependent conservative field
156+
values[..., fields.abarrho] = abar * rho
163157

164158
def auxilliaryVariableUpdate(self, values: Q):
165159
fields = Q.fields
166160

167-
arho = values[..., fields.arho]
161+
abarrho = values[..., fields.abarrho]
168162
arho1 = values[..., fields.arho1]
169163
arho2 = values[..., fields.arho2]
164+
arho1d = values[..., fields.arho1d]
170165
rhoU = values[..., fields.rhoU]
171166
rhoV = values[..., fields.rhoV]
167+
ad = values[..., fields.ad]
172168

173-
rho = arho1 + arho2
174-
alpha = arho / rho
175-
176-
if np.any(alpha < 0.0):
177-
print(alpha)
178-
exit()
179-
# if np.any(alpha > 1.0):
180-
# print(np.where(alpha > 1))
181-
# print(alpha[np.where(alpha > 1)])
182-
# print(alpha)
183-
# exit()
169+
rho = arho1 + arho2 + arho1d
170+
abar = abarrho / rho
184171

185-
alphas = PhasePair(alpha, 1.0 - alpha)
172+
alphabars = PhasePair(abar, 1.0 - abar)
186173
arhos = PhasePair(arho1, arho2)
187174

188-
values[..., fields.alpha] = alpha
175+
values[..., fields.abar] = abar
189176
values[..., fields.rho] = rho
190177
values[..., fields.U] = rhoU / rho
191178
values[..., fields.V] = rhoV / rho
@@ -195,11 +182,10 @@ def auxilliaryVariableUpdate(self, values: Q):
195182
for phase in Phases:
196183
phase_values = values.view(Q).get_phase(phase)
197184

198-
alpha = alphas[phase]
185+
alphabar = alphabars[phase]
199186
arho = arhos[phase]
200187

201-
rho = np.full_like(alpha, np.nan)
202-
np.divide(arho, alpha, where=alpha > 0, out=rho)
188+
rho = arho / alphabar / (1 - ad)
203189
p = self.problem.eos[phase].p(rho)
204190
c = self.problem.eos[phase].sound_velocity(rho)
205191

@@ -210,27 +196,35 @@ def auxilliaryVariableUpdate(self, values: Q):
210196
phase,
211197
phase_values,
212198
)
213-
if np.invert(np.isnan(c)):
214-
c_sq += arho * c**2
215199

216-
values[..., fields.c] = np.sqrt(c_sq / values[..., fields.rho])
200+
c_sq += arho * c**2
217201

218-
alpha = values[..., fields.alpha]
202+
values[..., fields.cFd] = np.sqrt(c_sq / values[..., fields.rho]) / (1 - ad)
203+
204+
abar = values[..., fields.abar]
219205
p1 = values[..., fields.p1]
220206
p2 = values[..., fields.p2]
221-
values[..., fields.P] = np.nan
222-
values[..., fields.P] = np.where(
223-
(alpha > 0) * (alpha < 1),
224-
alpha * p1 + (1 - alpha) * p2,
225-
np.where(alpha == 0, p2, p1),
207+
values[..., fields.pbar] = np.nan
208+
values[..., fields.pbar] = np.where(
209+
(abar > 0) * (abar < 1),
210+
abar * p1 + (1 - abar) * p2,
211+
np.where(abar == 0, p2, p1),
226212
)
227213

228214
def post_extrapolation(self, values: Q):
229-
self.prim2Q(values)
230-
# self.relaxation(values)
215+
"""During the step we update the conservative values. After the
216+
step we update the non-conservative variables. This method updates
217+
the values of the non-conservative (auxiliary) variables using the
218+
:class:`~.EOS`
219+
"""
220+
221+
# auxilliary variables update
222+
self.auxilliaryVariableUpdate(values)
223+
224+
self.relaxation(values)
231225

232226
# auxilliary variables update
233-
# self.auxilliaryVariableUpdate(values)
227+
self.auxilliaryVariableUpdate(values)
234228

235229
def post_step(self, values: Q):
236230
"""During the step we update the conservative values. After the
@@ -245,6 +239,28 @@ def post_step(self, values: Q):
245239
# auxilliary variables update
246240
self.auxilliaryVariableUpdate(values)
247241

242+
def CFL(
243+
self,
244+
cells: MeshCellSet,
245+
CFL_value,
246+
) -> float:
247+
dt = super().CFL(cells, CFL_value)
248+
249+
dx = cells.min_length
250+
251+
# Get the velocity components
252+
UV_slice = slice(Q.fields.U, Q.fields.V + 1)
253+
UV = cells.values[..., UV_slice]
254+
255+
U = np.linalg.norm(UV, axis=-1, keepdims=True)
256+
c = cells.values[..., Q.fields.cFd]
257+
258+
sigma = np.max(np.abs(U) + c[..., np.newaxis])
259+
260+
dt = np.min((dt, CFL_value * dx / sigma))
261+
262+
return dt
263+
248264

249265
class Rusanov(TSFourEqScheme):
250266
def intercellFlux(
@@ -286,8 +302,8 @@ def intercellFlux(
286302
# Find the normal velocity
287303
U = np.einsum("...mkl,...l->...mk", UV, normals)
288304
U_neigh = np.einsum("...mkl,...l->...mk", UV_neighs, normals)
289-
c = Q_L[..., fields.c]
290-
c_neigh = Q_R[..., fields.c]
305+
c = Q_L[..., fields.cFd]
306+
c_neigh = Q_R[..., fields.cFd]
291307

292308
# Let's retrieve the values of the sigma for current cell
293309
sigma = EulerRusanov.compute_sigma(U, U_neigh, c, c_neigh)
@@ -307,25 +323,3 @@ def intercellFlux(
307323
)
308324

309325
return FS
310-
311-
def CFL(
312-
self,
313-
cells: MeshCellSet,
314-
CFL_value,
315-
) -> float:
316-
dt = super().CFL(cells, CFL_value)
317-
318-
dx = cells.min_length
319-
320-
# Get the velocity components
321-
UV_slice = slice(Q.fields.U, Q.fields.V + 1)
322-
UV = cells.values[..., [0], UV_slice]
323-
324-
U = np.linalg.norm(UV, axis=-1)
325-
c = cells.values[..., [0], Q.fields.c]
326-
327-
sigma = np.max(np.abs(U) + c[..., np.newaxis])
328-
329-
dt = np.min((dt, CFL_value * dx / sigma))
330-
331-
return dt

0 commit comments

Comments
 (0)