wolfhece 2.2.2__py3-none-any.whl → 2.2.4__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.
- wolfhece/PandasGrid.py +67 -0
- wolfhece/PyDraw.py +732 -34
- wolfhece/PyGui.py +6 -2
- wolfhece/PyPalette.py +10 -0
- wolfhece/PyVertex.py +15 -2
- wolfhece/PyWMS.py +72 -3
- wolfhece/acceptability/acceptability_gui.py +99 -155
- wolfhece/acceptability/func.py +88 -8
- wolfhece/apps/check_install.py +6 -6
- wolfhece/apps/splashscreen.py +5 -0
- wolfhece/apps/version.py +1 -1
- wolfhece/assets/__init__.py +1 -0
- wolfhece/assets/speedometer.py +135 -0
- wolfhece/dike.py +684 -0
- wolfhece/drowning_victims/__init__.py +0 -0
- wolfhece/drowning_victims/drowning_class.py +2124 -0
- wolfhece/drowning_victims/drowning_functions.py +1019 -0
- wolfhece/lifewatch.py +88 -0
- wolfhece/scenario/config_manager.py +4 -4
- wolfhece/wolf_array.py +34 -11
- wolfhece/wolf_texture.py +9 -2
- {wolfhece-2.2.2.dist-info → wolfhece-2.2.4.dist-info}/METADATA +3 -1
- {wolfhece-2.2.2.dist-info → wolfhece-2.2.4.dist-info}/RECORD +26 -18
- {wolfhece-2.2.2.dist-info → wolfhece-2.2.4.dist-info}/WHEEL +0 -0
- {wolfhece-2.2.2.dist-info → wolfhece-2.2.4.dist-info}/entry_points.txt +0 -0
- {wolfhece-2.2.2.dist-info → wolfhece-2.2.4.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,1019 @@
|
|
1
|
+
import numpy as np
|
2
|
+
from numpy import random as rnd
|
3
|
+
import math
|
4
|
+
from scipy.stats import beta
|
5
|
+
from scipy.optimize import fsolve
|
6
|
+
import random
|
7
|
+
from wolfgpu.glsimulation import ResultsStore
|
8
|
+
from pathlib import Path
|
9
|
+
import json
|
10
|
+
import wx
|
11
|
+
import queue
|
12
|
+
import os
|
13
|
+
import logging
|
14
|
+
|
15
|
+
EPS = 0.15
|
16
|
+
G = [0, 0, -9.81]
|
17
|
+
P_ATM = 101325 # Pa
|
18
|
+
RHO_F = 1000
|
19
|
+
|
20
|
+
def beta_find(x, *data):
|
21
|
+
"""
|
22
|
+
Iterative function that finds the parameters alpha and beta defining a beta distribution and scales it in a range.
|
23
|
+
The function fits the parameters based on two given percentiles of the data.
|
24
|
+
|
25
|
+
:param x: Array containing initial guesses for alpha and beta.
|
26
|
+
:param data: Tuple containing (down, up, mini, maxi, perc1, perc2).
|
27
|
+
:return: Array containing the differences between the calculated and given percentiles.
|
28
|
+
"""
|
29
|
+
|
30
|
+
##Conversion of data into understandable parameters
|
31
|
+
down = data[0]
|
32
|
+
up = data[1]
|
33
|
+
mini = data[2]
|
34
|
+
maxi = data[3]
|
35
|
+
perc1 = data[4]
|
36
|
+
perc2 = data[5]
|
37
|
+
data = (down, up, mini, maxi, perc1, perc2)
|
38
|
+
|
39
|
+
## Initialisation of the iterative array with alpha and beta values into F[0] and F[1]
|
40
|
+
F = np.zeros((2))
|
41
|
+
|
42
|
+
## Calculation of the beta distribution with the given parameters
|
43
|
+
F[0] = beta.cdf((down - mini) / (maxi - mini), x[0], x[1]) - perc1
|
44
|
+
F[1] = beta.cdf((up - mini) / (maxi - mini), x[0], x[1]) - perc2
|
45
|
+
|
46
|
+
return F
|
47
|
+
|
48
|
+
def Body_motion(a_RK, batch_turb, CFL, Delta, epsilon, H_pre, H_post, Human, k, NbX, NbY, Pos_bp, resurface, sinking, time_b, t_Wolf_pre, t_Wolf_post, time_goal, U_bp, Ux_pre, Ux_post, Uy_pre, Uy_post, Z_param):
|
49
|
+
"""
|
50
|
+
Function calculating the motion of the body at each time step using a Runge-Kutta method.
|
51
|
+
From body position, velocity and flow environment, the function determines the flow velocity at the body position and calculates its new velocities, checking for collisions with walls.
|
52
|
+
|
53
|
+
:param a_RK: Runge-Kutta coefficient.
|
54
|
+
:param batch_turb: Batch turbulence.
|
55
|
+
:param CFL: Courant-Friedrichs-Lewy number.
|
56
|
+
:param Delta: Array of delta values.
|
57
|
+
:param epsilon: Epsilon value.
|
58
|
+
:param H_pre: Pre-update water depth.
|
59
|
+
:param H_post: Post-update water depth.
|
60
|
+
:param Human: Human parameters array.
|
61
|
+
:param k: Turbulence kinetic energy.
|
62
|
+
:param NbX: Number of cells in X direction.
|
63
|
+
:param NbY: Number of cells in Y direction.
|
64
|
+
:param Pos_bp: Body position array for calculations.
|
65
|
+
:param resurface: Resurface array.
|
66
|
+
:param sinking: Sinking array.
|
67
|
+
:param time_b: Time array for savings.
|
68
|
+
:param t_Wolf_pre: Pre-update Wolf time.
|
69
|
+
:param t_Wolf_post: Post-update Wolf time.
|
70
|
+
:param time_goal: Time goal.
|
71
|
+
:param U_bp: Body velocity array for calculations.
|
72
|
+
:param Ux_pre: Pre-update X velocity.
|
73
|
+
:param Ux_post: Post-update X velocity.
|
74
|
+
:param Uy_pre: Pre-update Y velocity.
|
75
|
+
:param Uy_post: Post-update Y velocity.
|
76
|
+
:param Z_param: Z parameters array.
|
77
|
+
:return: Updated Human, Pos_b, resurface, sinking, time_b, U_b arrays.
|
78
|
+
"""
|
79
|
+
|
80
|
+
##Global parameters and conersion of some parameters to give them an explicit name
|
81
|
+
|
82
|
+
vertical = Z_param[0,3]
|
83
|
+
turb_type = 1
|
84
|
+
z_0 = Z_param[0,2]
|
85
|
+
T_w = Z_param[:,5]
|
86
|
+
|
87
|
+
dt_Wolf = t_Wolf_post - t_Wolf_pre
|
88
|
+
t_Wolf_perc = (time_b - t_Wolf_pre)/dt_Wolf
|
89
|
+
t_Wolf_perc_insta = t_Wolf_perc + Delta[3]/dt_Wolf
|
90
|
+
|
91
|
+
## Body parameters
|
92
|
+
|
93
|
+
m_b = Human[:,16] + Human[:,7]
|
94
|
+
|
95
|
+
BSA_Ken = 2.4631 #[m²]
|
96
|
+
|
97
|
+
CAM = Human[:,3]
|
98
|
+
CDA = Human[:,4] * Human[:,2]/BSA_Ken
|
99
|
+
CLA = Human[:,5] * Human[:,2]/BSA_Ken
|
100
|
+
CSA = np.random.choice([-1, 1], size=len(Human[:,23]))*Human[:,23] * Human[:,2]/BSA_Ken
|
101
|
+
|
102
|
+
mu_stat = Z_param[:,3]
|
103
|
+
|
104
|
+
## Initisalisation of the Runge-Kutta variables as variables at the time t for both columns
|
105
|
+
|
106
|
+
Pos_RK = np.array([Pos_bp.copy(),Pos_bp.copy()])
|
107
|
+
U_RK = np.array([U_bp.copy(),U_bp.copy()])
|
108
|
+
acc_RK = np.zeros((2,U_bp.shape[0],U_bp.shape[1]))
|
109
|
+
|
110
|
+
for i in range(2): #RK loop
|
111
|
+
|
112
|
+
j = abs(i-1)
|
113
|
+
|
114
|
+
index_bp = ((np.floor(Pos_RK[j,:,:].T / Delta[0:3,None])).T).astype(int) #Body position converted into index in the input matrix of U and H
|
115
|
+
|
116
|
+
## Get the velocity at exact time and horizontal position of each run, most time consuming function of the drowning calculation
|
117
|
+
[du_insta,_,_,H_f,k_f,U_x_vec,U_y_vec,walls] = interp_mailles_mat(Delta,epsilon,H_pre,H_post,index_bp,k,NbX,NbY,Pos_RK[j,:,:],t_Wolf_perc,t_Wolf_perc_insta,Ux_pre,Ux_post,Uy_pre,Uy_post)
|
118
|
+
|
119
|
+
## Parameters and calculations to have the velocity at the body vertical position and its relative velocity
|
120
|
+
l_tilde = EPS/H_f
|
121
|
+
z = np.clip(Pos_RK[j, :, -1],EPS, H_f)
|
122
|
+
z_tilde = np.maximum(z/H_f,l_tilde)
|
123
|
+
mu = np.minimum(mu_stat * ((1-z_tilde)/z_tilde * l_tilde/(1-l_tilde))**3,mu_stat)
|
124
|
+
|
125
|
+
U_max = np.sqrt(U_x_vec**2 + U_y_vec**2)
|
126
|
+
dt = np.clip(np.divide(CFL * Delta[4],np.where(U_max!=0,U_max,Delta[3])), 0.00001, Delta[3]) #to be used if turb_type == 2 or 3 in Flow_time_t
|
127
|
+
U_x_dif,U_x_sign,U_y_dif,U_y_sign = Flow_time_t(batch_turb,dt,EPS,H_f,k_f,turb_type,U_RK[j,:,:],U_x_vec,U_y_vec,z,z_0)
|
128
|
+
norm_U = np.sqrt((U_x_dif**2 + U_y_dif**2))
|
129
|
+
|
130
|
+
dt = np.clip(np.divide(CFL * Delta[4],np.where(norm_U!=0, norm_U, Delta[3])), 0.0001, Delta[3]).T
|
131
|
+
|
132
|
+
V_b = Body_volume_variation(Z_param[:,7],Human[:,2],Human[:,14],H_f,Human[:,16],time_b,T_w,1.5*Human[:,15],Human[:,18],Human[:,20],Human[:,21],z)
|
133
|
+
|
134
|
+
acc_RK[i,:,:],U_RK[i,:,:] = Motion_equations(CAM,CDA,CLA,CSA,dt,du_insta,m_b,mu,U_RK[j,:,:],U_x_dif,U_x_sign,U_y_dif,U_y_sign,V_b,vertical)
|
135
|
+
Pos_RK[i,:,:] += U_RK[i,:,:]*dt[:,None]
|
136
|
+
|
137
|
+
mask_bottom = ((U_RK[i, :, 2] < 0) & (Pos_RK[i, :, 2] < EPS)) #Mask to consider if the body goes down while it is on the bottom
|
138
|
+
Pos_RK[i, :, 2][mask_bottom] = EPS #Prevents the body from going under the bottom
|
139
|
+
U_RK[i, :, 2][mask_bottom] = 0
|
140
|
+
|
141
|
+
mask_up = ((U_RK[i, :, 2] > 0) & (Pos_RK[i, :, 2] > H_f)) #Mask to consider if the body goes down while it is on the bottom
|
142
|
+
Pos_RK[i, :, 2][mask_up] = H_f[mask_up] #Prevents the body from going under the bottom
|
143
|
+
U_RK[i, :, 2][mask_up] = 0
|
144
|
+
|
145
|
+
t_Wolf_perc += dt/dt_Wolf
|
146
|
+
|
147
|
+
|
148
|
+
U_b = (1-a_RK)*U_RK[0,:,:] + a_RK*U_RK[1,:,:]
|
149
|
+
Pos_b = (1-a_RK)*Pos_RK[0,:,:] + a_RK*Pos_RK[1,:,:]
|
150
|
+
|
151
|
+
time_b += dt
|
152
|
+
|
153
|
+
## Check and corrections for collisions with walls
|
154
|
+
index_b = ((np.floor(Pos_b[:,:2].T / Delta[0:2,None])).T).astype(int)
|
155
|
+
walls = (H_pre[index_b[:,1], index_b[:,0]] < EPS)
|
156
|
+
ind_walls = np.where(walls)[0]
|
157
|
+
if ind_walls.size!=0:
|
158
|
+
[Pos_b[ind_walls,0:2],U_b[ind_walls,0:2]] = Collision(Delta,Pos_b[ind_walls,0:2],Pos_bp[ind_walls,0:2],U_b[ind_walls,0:2],walls)
|
159
|
+
index_b = ((np.floor(Pos_b[:,:2].T / Delta[0:2,None])).T).astype(int)
|
160
|
+
walls = (H_pre[index_b[:,1], index_b[:,0]] < EPS)
|
161
|
+
ind_walls = np.where(walls)[0]
|
162
|
+
U_b[ind_walls,0:2] = 0
|
163
|
+
|
164
|
+
|
165
|
+
## Storage of times when bodies sank and resurfaced
|
166
|
+
sinking[:,0] = sinking[:,0] + (sinking[:,0]==0)*(Pos_b[:,2] == EPS)*(U_b[:,2]==0) * Pos_b[:,0]
|
167
|
+
sinking[:,1] = sinking[:,1] + (sinking[:,1]==0)*(Pos_b[:,2] == EPS)*(U_b[:,2]==0) * time_b
|
168
|
+
|
169
|
+
H_f = H_f.ravel()
|
170
|
+
V_b = V_b.ravel()
|
171
|
+
resurface[:,0] = resurface[:,0] + (resurface[:,0]==0)*(Pos_b[:,2] >= H_f-0.5)*(m_b/V_b/RHO_F<1) * Pos_b[:,0]
|
172
|
+
resurface[:,1] = resurface[:,1] + (resurface[:,1]==0)*(Pos_b[:,2] >= H_f-0.5)*(m_b/V_b/RHO_F<1) * time_b
|
173
|
+
|
174
|
+
return Human,Pos_b,resurface,sinking,time_b,U_b
|
175
|
+
|
176
|
+
def Body_temperature(BSA, mass, T_w, t):
|
177
|
+
"""
|
178
|
+
Gives the body temperature at time t considering ONLY the conduction.
|
179
|
+
|
180
|
+
:param BSA: Body surface area [m²].
|
181
|
+
:param mass: Body mass [kg].
|
182
|
+
:param T_w: Water temperature [°C].
|
183
|
+
:param t: Time from the beginning.
|
184
|
+
:return: ADD (Accumulated Degree Days) and body temperature.
|
185
|
+
"""
|
186
|
+
T_body_ini = 37
|
187
|
+
|
188
|
+
# c_b = 3500 # J/kg/K
|
189
|
+
# t_skin = 4.6e-3 # Average thickness of the skin in meters
|
190
|
+
# k_skin = 0.21 # Thermal conductivity of the skin W/m K
|
191
|
+
|
192
|
+
# R_cond = t_skin / (k_skin * BSA)
|
193
|
+
|
194
|
+
# Res = R_cond * mass * c_b
|
195
|
+
|
196
|
+
Res = 3000
|
197
|
+
|
198
|
+
T_b = T_w + (T_body_ini - T_w)*np.exp(-t/Res)
|
199
|
+
|
200
|
+
ADD = ((T_body_ini - T_w) * Res * (1 - np.exp(-t / Res)) + T_w * t)/3600/24 #Integral of the body temperature as a function of time, ADD in [°C.day]
|
201
|
+
|
202
|
+
return ADD,T_b
|
203
|
+
|
204
|
+
def Body_volume_variation(alpha_1, BSA, FRC, H_f, m_b, time_b, T_w, TLC, V_b0, V_clothes1, V_clothes2, z):
|
205
|
+
"""
|
206
|
+
Function calculating the body volume variation due to the putrefaction gases.
|
207
|
+
The method is described in Delhez et al. 2025, "Predicting the buoyancy and Postmortem Submersion Interval of victims of river drowning".
|
208
|
+
|
209
|
+
:param alpha_1: Alpha parameter.
|
210
|
+
:param BSA: Body surface area.
|
211
|
+
:param FRC: Functional residual capacity.
|
212
|
+
:param H_f: Water depth.
|
213
|
+
:param m_b: Body mass.
|
214
|
+
:param time_b: Time array for savings.
|
215
|
+
:param T_w: Water temperature.
|
216
|
+
:param TLC: Total lung capacity.
|
217
|
+
:param V_b0: Initial body volume.
|
218
|
+
:param V_clothes1: Clothes volume for temperature < 15°C.
|
219
|
+
:param V_clothes2: Clothes volume for temperature >= 15°C.
|
220
|
+
:param z: Depth array.
|
221
|
+
:return: Body volume.
|
222
|
+
"""
|
223
|
+
|
224
|
+
p_hydro = np.maximum(H_f - z,0.00001)*(-G[2])*1000
|
225
|
+
p_ext = P_ATM+p_hydro
|
226
|
+
V_s = V_b0 - FRC #fraction of FRC on the whole body volume
|
227
|
+
n_0 = P_ATM*FRC/(8.3144621*(37+273.15)) #initial amount of gas in the lungs, perfect gas law
|
228
|
+
|
229
|
+
ADD = time_b/60/60/24 * T_w
|
230
|
+
_,T_b = Body_temperature(BSA,m_b,T_w,time_b) #Consideration of the actual body temperature
|
231
|
+
ratio_ADD = ADD/alpha_1
|
232
|
+
|
233
|
+
n_1 = 2*P_ATM * TLC/(8.3144621*(T_w+273.15))
|
234
|
+
coef_eta = [10,-15,6]
|
235
|
+
eta = n_1*(coef_eta[2]*(ratio_ADD)**5+coef_eta[1]*(ratio_ADD)**4+coef_eta[0]*(ratio_ADD)**3)
|
236
|
+
eta = np.minimum(eta,n_1)
|
237
|
+
|
238
|
+
V_clothes = np.where(T_w < 15, V_clothes1, V_clothes2)
|
239
|
+
|
240
|
+
V_comp = (n_0+eta/n_1*(n_1-n_0))*8.3144621*(T_b+273.15)/(p_ext) #Compressible Volume
|
241
|
+
V_b = V_s + V_comp + V_clothes
|
242
|
+
|
243
|
+
return V_b
|
244
|
+
|
245
|
+
def Collision(Delta, Pos, Pos_p, U, walls):
|
246
|
+
"""
|
247
|
+
Correct the bodies that hit a wall by correcting its velocity and replace it on the wall (+marge).
|
248
|
+
|
249
|
+
:param Delta: Array of delta values.
|
250
|
+
:param Pos: Body position array for calculations.
|
251
|
+
:param Pos_p: Previous body position array.
|
252
|
+
:param U: Body velocity array for calculations.
|
253
|
+
:param walls: Walls array.
|
254
|
+
:return: Corrected position and velocity arrays.
|
255
|
+
"""
|
256
|
+
marge = Delta[0:2]/100 * (25)
|
257
|
+
elasticite = 0.1
|
258
|
+
|
259
|
+
Correction_pos_val = Pos%Delta[0:2]-Delta[0:2]/2 #Detection of body position in the cell
|
260
|
+
Correction_pos_sign = ((Pos%Delta[0:2]>Delta[0:2]/2) != (Pos_p%Delta[0:2]>Delta[0:2]/2)) #1 for right or below, 0 for left and above
|
261
|
+
# which_direction = (U>0 and walls[1:3]==0) #if 1, collision with wall right and/or above; if 0, collision with a wall left and/or below
|
262
|
+
|
263
|
+
adjust_sign = (1-2*(Correction_pos_sign))
|
264
|
+
|
265
|
+
#Correction of body position and velocity
|
266
|
+
U = (U * adjust_sign)*(elasticite)*Correction_pos_sign + U * (Correction_pos_sign==0)
|
267
|
+
Pos += (np.sign(Correction_pos_val)*(Correction_pos_sign==1)*((Delta[0:2]/2-np.abs(Correction_pos_val))+marge))
|
268
|
+
|
269
|
+
return Pos,U
|
270
|
+
|
271
|
+
def Flow_time_t(batch_turb, dt, epsilon, H_f, k, turb_type, U_bp, U_x_vec, U_y_vec, z, z_0):
|
272
|
+
"""
|
273
|
+
Calculates the difference between the body velocity and the flow velocity (-> relative body velocity) and its sign before and after evaluating the turbulences.
|
274
|
+
|
275
|
+
:param batch_turb: Batch turbulence.
|
276
|
+
:param dt: Time step.
|
277
|
+
:param epsilon: Epsilon value.
|
278
|
+
:param H_f: Water depth.
|
279
|
+
:param k: Turbulence kinetic energy.
|
280
|
+
:param turb_type: Turbulence type.
|
281
|
+
:param U_bp: Body velocity array for calculations.
|
282
|
+
:param U_x_vec: X velocity vector.
|
283
|
+
:param U_y_vec: Y velocity vector.
|
284
|
+
:param z: Depth array.
|
285
|
+
:param z_0: Roughness length.
|
286
|
+
:return: U_x_dif, U_x_sign, U_y_dif, U_y_sign arrays.
|
287
|
+
"""
|
288
|
+
U_x_dif = (U_x_vec-U_bp[:,0])
|
289
|
+
U_y_dif = (U_y_vec-U_bp[:,1])
|
290
|
+
U_x_vec,U_y_vec = U_turbulence(batch_turb,dt,epsilon,H_f,k,turb_type,U_x_vec,U_x_dif,U_y_vec,U_y_dif,z,z_0)
|
291
|
+
U_x_dif = (U_x_vec-U_bp[:,0])
|
292
|
+
U_y_dif = (U_y_vec-U_bp[:,1])
|
293
|
+
U_x_sign = np.sign(U_x_dif)
|
294
|
+
U_y_sign = np.sign(U_y_dif)
|
295
|
+
|
296
|
+
return U_x_dif,U_x_sign,U_y_dif,U_y_sign
|
297
|
+
|
298
|
+
def interp_mailles_mat(Delta, epsilon, H_0, H_1, index_b, k, NbX, NbY, Pos_b, t_WOLF_perc, t_Wolf_perc_insta, Ux_0, Ux_1, Uy_0, Uy_1):
|
299
|
+
"""
|
300
|
+
Determines the flow velocity and height at the body position based on the value of the cells in which the body is and the next cells in x, y and xy.
|
301
|
+
It is a spatial bi-linear and temporal linear interpolation.
|
302
|
+
Method described in Delhez et al. 2025, Lagrangian modelling of the drift of a victim of river drowning.
|
303
|
+
|
304
|
+
:param Delta: Array of delta values.
|
305
|
+
:param epsilon: Epsilon value.
|
306
|
+
:param H_0: Pre-update water depth.
|
307
|
+
:param H_1: Post-update water depth.
|
308
|
+
:param index_b: Body position indices.
|
309
|
+
:param k: Turbulence kinetic energy.
|
310
|
+
:param NbX: Number of cells in X direction.
|
311
|
+
:param NbY: Number of cells in Y direction.
|
312
|
+
:param Pos_b: Body position array for savings.
|
313
|
+
:param t_WOLF_perc: Percentage of Wolf time.
|
314
|
+
:param t_Wolf_perc_insta: Instantaneous percentage of Wolf time.
|
315
|
+
:param Ux_0: Pre-update X velocity.
|
316
|
+
:param Ux_1: Post-update X velocity.
|
317
|
+
:param Uy_0: Pre-update Y velocity.
|
318
|
+
:param Uy_1: Post-update Y velocity.
|
319
|
+
:return: du_insta, epsilon_v, ind_walls, H_v, k_v, Ux_v, Uy_v, walls arrays.
|
320
|
+
"""
|
321
|
+
|
322
|
+
# Calculate the position within the cell
|
323
|
+
x_rel = (Pos_b[:, 0] % Delta[0]) - Delta[0] / 2
|
324
|
+
y_rel = (Pos_b[:, 1] % Delta[1]) - Delta[1] / 2
|
325
|
+
|
326
|
+
# Determine next cell indices
|
327
|
+
index_bo_x = np.array(index_b[:, 0] + np.sign(x_rel),dtype=int)
|
328
|
+
index_bo_y = np.array(index_b[:, 1] + np.sign(y_rel),dtype=int)
|
329
|
+
|
330
|
+
# Normalize x and y to the range [0, 0.5]
|
331
|
+
x = np.abs(x_rel) / Delta[0]
|
332
|
+
y = np.abs(y_rel) / Delta[1]
|
333
|
+
|
334
|
+
# Check for non-zero Ux values
|
335
|
+
walls = np.column_stack((Ux_0[index_b[:,1], index_b[:,0]] != 0.0,Ux_0[index_b[:,1], index_bo_x] != 0.0,Ux_0[index_bo_y, index_b[:,0]] != 0.0,Ux_0[index_bo_y, index_bo_x] != 0.0))
|
336
|
+
ind_walls = np.where(~np.all(walls, axis=1))[0]
|
337
|
+
|
338
|
+
fact_cell = (1 - x) * (1 - y)
|
339
|
+
fact_next_x = x * (1 - y)
|
340
|
+
fact_next_y = (1 - x) * y
|
341
|
+
fact_next_xy = x * y
|
342
|
+
|
343
|
+
# Interpolate values (spatially)
|
344
|
+
def interp(var):
|
345
|
+
return (var[index_b[:,1],index_b[:,0]] * fact_cell +
|
346
|
+
var[index_b[:,1],index_bo_x] * fact_next_x +
|
347
|
+
var[index_bo_y,index_b[:,0]] * fact_next_y +
|
348
|
+
var[index_bo_y,index_bo_x] * fact_next_xy)
|
349
|
+
|
350
|
+
interp_Ux_0 = interp(Ux_0)
|
351
|
+
interp_Ux_1 = interp(Ux_1)
|
352
|
+
interp_Uy_0 = interp(Uy_0)
|
353
|
+
interp_Uy_1 = interp(Uy_1)
|
354
|
+
|
355
|
+
# Temporal interpolation
|
356
|
+
Ux_v = interp_Ux_0*(1-t_WOLF_perc) + interp_Ux_1*t_WOLF_perc
|
357
|
+
Uy_v = interp_Uy_0*(1-t_WOLF_perc) + interp_Uy_1*t_WOLF_perc
|
358
|
+
H_v = interp(H_0)*(1-t_WOLF_perc) + interp(H_1)*t_WOLF_perc
|
359
|
+
k_v = 0 #interp(k)
|
360
|
+
epsilon_v = 0 #interp(epsilon)
|
361
|
+
|
362
|
+
first_part = np.array(Ux_v - (interp_Ux_0*(1 - t_Wolf_perc_insta) + interp_Ux_1*t_Wolf_perc_insta))
|
363
|
+
second_part = np.array(Uy_v - (interp_Uy_0*(1 - t_Wolf_perc_insta) + interp_Uy_1*t_Wolf_perc_insta))
|
364
|
+
du_insta = np.column_stack((first_part, second_part))
|
365
|
+
|
366
|
+
# Variant if a body is next to a wall
|
367
|
+
if ind_walls.size != 0:
|
368
|
+
active_cells = np.maximum(np.sum(walls, axis=1), 1)
|
369
|
+
|
370
|
+
def sum_vars(var):
|
371
|
+
return (var[index_b[ind_walls,1],index_b[ind_walls,0]] +
|
372
|
+
var[index_b[ind_walls,1],index_bo_x[ind_walls]] +
|
373
|
+
var[index_bo_y[ind_walls],index_b[ind_walls,0]] +
|
374
|
+
var[index_bo_y[ind_walls],index_bo_x[ind_walls]])
|
375
|
+
|
376
|
+
# sum_vars = lambda var: (var[index_b[ind_walls,1],index_b[ind_walls,0]] +
|
377
|
+
# var[index_b[ind_walls,1],index_bo_x[ind_walls]] +
|
378
|
+
# var[index_bo_y[ind_walls],index_b[ind_walls,0]] +
|
379
|
+
# var[index_bo_y[ind_walls],index_bo_x[ind_walls]))
|
380
|
+
|
381
|
+
Ux_v[ind_walls] = sum_vars(Ux_0) / active_cells[ind_walls]
|
382
|
+
Uy_v[ind_walls] = sum_vars(Uy_0) / active_cells[ind_walls]
|
383
|
+
H_v[ind_walls] = sum_vars(H_0) / active_cells[ind_walls]
|
384
|
+
# k_v[ind_walls] = sum_vars(k) / active_cells[ind_walls]
|
385
|
+
# epsilon_v[ind_walls] = sum_vars(epsilon) / active_cells[ind_walls]
|
386
|
+
|
387
|
+
# limit_layer = lambda var: (var[index_cell[ind_walls]]*(0.5-x[ind_walls])*(walls[ind_walls,1]==0) +
|
388
|
+
# var[index_cell[ind_walls]]*(0.5-y[ind_walls])*(walls[ind_walls,2]==0))
|
389
|
+
|
390
|
+
# Ux_v[ind_walls] = limit_layer(Ux)
|
391
|
+
# Uy_v[ind_walls] = limit_layer(Uy)
|
392
|
+
# H_v[ind_walls] = limit_layer(H)
|
393
|
+
# H_v[ind_walls] = (H_v[ind_walls]!=0)*H_v[ind_walls] + (H_v[ind_walls]==0)*1
|
394
|
+
# k_v[ind_walls] = limit_layer(k)
|
395
|
+
# epsilon_v[ind_walls] = limit_layer(epsilon)
|
396
|
+
|
397
|
+
H_v = np.maximum(H_v,EPS*2)
|
398
|
+
|
399
|
+
ind_walls = np.where(walls[:, 0] == 0)[0]
|
400
|
+
|
401
|
+
return du_insta,epsilon_v,ind_walls, H_v, k_v, Ux_v, Uy_v,walls[ind_walls,:]
|
402
|
+
|
403
|
+
def known_1(g, mini, maxi, down, up, perc1, perc2):
|
404
|
+
"""
|
405
|
+
Used in the fit of the beta parameters alpha and beta by iteration.
|
406
|
+
|
407
|
+
:param g: Guess array.
|
408
|
+
:param mini: Minimum value.
|
409
|
+
:param maxi: Maximum value.
|
410
|
+
:param down: Lower percentile value.
|
411
|
+
:param up: Upper percentile value.
|
412
|
+
:param perc1: Lower percentile.
|
413
|
+
:param perc2: Upper percentile.
|
414
|
+
:return: Tuple containing alpha and beta.
|
415
|
+
"""
|
416
|
+
x0 = np.array([1, 1])
|
417
|
+
data = (down, up, mini, maxi, perc1, perc2)
|
418
|
+
x = fsolve(beta_find, x0, args=data)
|
419
|
+
a = x[0]
|
420
|
+
b = x[1]
|
421
|
+
return (a, b)
|
422
|
+
|
423
|
+
def Loading(Path_loading, Pos_b, time_b, U_b):
|
424
|
+
"""
|
425
|
+
Loads the results of a previous simulation and returns the data needed to start from these results.
|
426
|
+
|
427
|
+
:param Path_loading: Path to the loading file.
|
428
|
+
:param Pos_b: Body position array for savings.
|
429
|
+
:param time_b: Time array for savings.
|
430
|
+
:param U_b: Body velocity array for savings.
|
431
|
+
:return: count_initial, Human, n_loaded, Pos_b, time_b, U_b, Z_param arrays.
|
432
|
+
"""
|
433
|
+
n_b_wanted = np.size(Pos_b,0)
|
434
|
+
n_wanted = np.size(Pos_b,2)
|
435
|
+
|
436
|
+
Human = data["Human"]
|
437
|
+
Pos_b = data["Pos_b"]
|
438
|
+
time_b = data["time_b"]
|
439
|
+
U_b = data["U_b"]
|
440
|
+
Z_param = data["Z_param"]
|
441
|
+
|
442
|
+
n_b_loaded = np.size(Pos_b,0)
|
443
|
+
n_loaded = np.size(Pos_b,2)
|
444
|
+
|
445
|
+
count_initial = n_loaded-1
|
446
|
+
|
447
|
+
data = np.load(Path_loading)
|
448
|
+
|
449
|
+
if n_b_wanted != n_b_loaded:
|
450
|
+
print(f"Error: Size of the sample loaded is different from the new one, the new one must be made of {n_b_loaded} bodies")
|
451
|
+
return
|
452
|
+
|
453
|
+
Pos_b[:][:][n_loaded+1] = np.zeros((n_b_wanted,3,int(n_wanted-n_loaded)))
|
454
|
+
time_b[:][:][n_loaded+1] = np.zeros((n_b_wanted,3,int(n_wanted-n_loaded)))
|
455
|
+
U_b[:][:][n_loaded+1] = np.zeros((n_b_wanted,3,int(n_wanted-n_loaded)))
|
456
|
+
|
457
|
+
return count_initial,Human,n_loaded,Pos_b,time_b,U_b,Z_param
|
458
|
+
|
459
|
+
def Loop_management(progress_queue, process_id, a_RK, BC_cells, count, count_Wolf, CFL, Delta, Human_np, i_initial, n_b, n_saved, n_t, NbX, NbY, Path_Saving, Path_Wolf, Pos, Pos_b, resurface, sinking, time, time_b, time_goal, U, U_b, wanted_time, wanted_Wolf, Z_param_np):
|
460
|
+
"""
|
461
|
+
Main loop of the code. Calculates the motion of each body at each time in the loop and updates the flow when needed.
|
462
|
+
Everything is based on the array "still" which contains the index of all the bodies that need more calculations, as we work with variable time step for each body.
|
463
|
+
If a body is out of the domain, it is out of still; if a body reaches a time for which we need a save, it is out of still;
|
464
|
+
if a body reaches a time for which the flow needs to be updated, it is out of still.
|
465
|
+
|
466
|
+
:param progress_queue: Queue for progress updates.
|
467
|
+
:param process_id: Process ID.
|
468
|
+
:param a_RK: Runge-Kutta coefficient.
|
469
|
+
:param BC_cells: Boundary condition cells.
|
470
|
+
:param count: Count of saved states.
|
471
|
+
:param count_Wolf: Count of Wolf states.
|
472
|
+
:param CFL: Courant-Friedrichs-Lewy number.
|
473
|
+
:param Delta: Array of delta values.
|
474
|
+
:param Human_np: Human parameters array.
|
475
|
+
:param i_initial: Initial index.
|
476
|
+
:param n_b: Number of bodies.
|
477
|
+
:param n_saved: Number of saved states.
|
478
|
+
:param n_t: Number of time steps.
|
479
|
+
:param NbX: Number of cells in X direction.
|
480
|
+
:param NbY: Number of cells in Y direction.
|
481
|
+
:param Path_Saving: Path to save results.
|
482
|
+
:param Path_Wolf: Path to Wolf results.
|
483
|
+
:param Pos: Body position array for calculations.
|
484
|
+
:param Pos_b: Body position array for savings.
|
485
|
+
:param resurface: Resurface array.
|
486
|
+
:param sinking: Sinking array.
|
487
|
+
:param time: Time array for calculations.
|
488
|
+
:param time_b: Time array for savings.
|
489
|
+
:param time_goal: Time goal.
|
490
|
+
:param U: Body velocity array for calculations.
|
491
|
+
:param U_b: Body velocity array for savings.
|
492
|
+
:param wanted_time: Array of wanted times.
|
493
|
+
:param wanted_Wolf: Array of wanted Wolf times.
|
494
|
+
:param Z_param_np: Z parameters array.
|
495
|
+
:return: Updated Pos_b, resurface, sinking, time_b, U_b arrays.
|
496
|
+
"""
|
497
|
+
|
498
|
+
# Initialisation of some of the function variables
|
499
|
+
batch_turb = rnd.normal(0,1,n_b) # Vector with random values following a normal distribution used to avoid using rnd.normal at each iteration
|
500
|
+
still = np.arange(n_b)
|
501
|
+
i = i_initial+1
|
502
|
+
count_Wolf_ini = count_Wolf
|
503
|
+
|
504
|
+
# Loading of the first flow between t_initial and t_initial + dt_Wolf (often 1h)
|
505
|
+
H_pre,Ux_pre,Uy_pre = Read_Wolf_GPU_mat(count_Wolf+1,Path_Wolf)
|
506
|
+
H_post,Ux_post,Uy_post = Read_Wolf_GPU_mat(count_Wolf+2,Path_Wolf)
|
507
|
+
|
508
|
+
epsilon = H_pre*0
|
509
|
+
k = H_pre*0
|
510
|
+
|
511
|
+
# Main loop of the model
|
512
|
+
while count<(n_saved):
|
513
|
+
t_for = wanted_time[count]
|
514
|
+
t_Wolf = wanted_Wolf[count_Wolf]
|
515
|
+
s = 1 - i%2
|
516
|
+
sp = 0 + i%2
|
517
|
+
|
518
|
+
# Body position calculation at each time step
|
519
|
+
(Human_np[still,:],Pos[still,:,s],resurface[still,:],sinking[still,:],time[still],U[still,:,s]) = Body_motion(a_RK,batch_turb,CFL,Delta,epsilon,H_pre,H_post,Human_np[still,:],k,NbX,NbY,Pos[still,:,sp],resurface[still,:],sinking[still,:],time[still],t_Wolf-wanted_Wolf[count_Wolf_ini],wanted_Wolf[count_Wolf+1]-wanted_Wolf[count_Wolf_ini],t_for,U[still,:,sp],Ux_pre,Ux_post,Uy_pre,Uy_post,Z_param_np[still,:])
|
520
|
+
index = np.floor_divide(Pos[:, :2, s], Delta[:2]).astype(int)
|
521
|
+
still = np.where((time < t_for) & (time < t_Wolf) & (~np.any(np.all(index[:, None] == BC_cells, axis=2), axis=1)))[0]
|
522
|
+
|
523
|
+
# Save of calculations from the working variables (size(n,n,2) with index s being the time t and sp time t-dt to size(n,n,n_saved)) or need to update the flow
|
524
|
+
if still.size==0:
|
525
|
+
if np.any(time<t_Wolf-wanted_Wolf[count_Wolf_ini]):#Save data
|
526
|
+
Pos_b[:,:,count] = Pos[:,:,s]
|
527
|
+
U_b[:,:,count] = U[:,:,s]
|
528
|
+
time_b[:,count] = time
|
529
|
+
|
530
|
+
count += 1
|
531
|
+
t_for = wanted_time[count]
|
532
|
+
still = np.where((time < t_for) & (time < t_Wolf) & (~np.any(np.all(index[:, None] == BC_cells, axis=2), axis=1)))[0]
|
533
|
+
elif np.any(time<t_for): # Update flow
|
534
|
+
H_pre,Ux_pre,Uy_pre = Read_Wolf_GPU_mat(count_Wolf+1,Path_Wolf)
|
535
|
+
H_post,Ux_post,Uy_post = Read_Wolf_GPU_mat(count_Wolf+2,Path_Wolf)
|
536
|
+
count_Wolf += 1
|
537
|
+
|
538
|
+
t_Wolf = wanted_Wolf[count_Wolf]
|
539
|
+
still = np.where((time < t_for) & (time < t_Wolf) & (~np.any(np.all(index[:, None] == BC_cells, axis=2), axis=1)))[0]
|
540
|
+
else: # Save data and update flow
|
541
|
+
Pos_b[:,:,count] = Pos[:,:,s]
|
542
|
+
U_b[:,:,count] = U[:,:,s]
|
543
|
+
time_b[:,count] = time
|
544
|
+
|
545
|
+
count += 1
|
546
|
+
t_for = wanted_time[count]
|
547
|
+
if t_for > wanted_Wolf[count_Wolf+1]-wanted_Wolf[count_Wolf_ini]:
|
548
|
+
count_Wolf += 1
|
549
|
+
t_Wolf = wanted_Wolf[count_Wolf]
|
550
|
+
H_pre,Ux_pre,Uy_pre = Read_Wolf_GPU_mat(count_Wolf,Path_Wolf)
|
551
|
+
H_post,Ux_post,Uy_post = Read_Wolf_GPU_mat(count_Wolf+1,Path_Wolf)
|
552
|
+
still = np.where((time < t_for) & (time < t_Wolf) & (~np.any(np.all(index[:, None] == BC_cells, axis=2), axis=1)))[0]
|
553
|
+
|
554
|
+
if process_id != -1: #To not enter when we do not work in multiprocess
|
555
|
+
progress_queue.put((process_id, time[0])) #Sends the id and the time of this process to the main one
|
556
|
+
else:
|
557
|
+
Path_save = os.path.join(Path_Saving,'Results')
|
558
|
+
os.makedirs(Path_save,exist_ok=True)
|
559
|
+
Save_wanted_time(Path_save,Pos[:,:,s],U[:,:,s],count-1)
|
560
|
+
|
561
|
+
if still.size==0: #End of the loop
|
562
|
+
Pos_b[:, :, count:] = np.repeat(Pos[:, :, 0:1], repeats=n_saved-count, axis=2)
|
563
|
+
# time_b[:, count:] = np.repeat(time[:], repeats=n_saved-count, axis=2)
|
564
|
+
print(f"[Process {process_id}] End of simulation")
|
565
|
+
count = n_saved + 1
|
566
|
+
break
|
567
|
+
|
568
|
+
# pbar.update(int(time[0] - pbar.n))
|
569
|
+
i += 1
|
570
|
+
|
571
|
+
return Pos_b,resurface,sinking,time_b,U_b
|
572
|
+
|
573
|
+
def Motion_equations(CAM, CDA, CLA, CSA, dt, du_insta, m_b, mu, U_bp, U_x_dif, U_x_sign, U_y_dif, U_y_sign, V_b, vertical):
|
574
|
+
"""
|
575
|
+
Calculates the body acceleration and velocity based on the motion equation with the Flow to the body forces (Drag, Side and Lift), Gravity and Buoyancy, Friction with the bottom and added mass effect.
|
576
|
+
Equations described in Delhez et al., 2025 "Lagrangian modelling of the drift of a victim of river drowning"
|
577
|
+
|
578
|
+
:param CAM: Added mass coefficient.
|
579
|
+
:param CDA: Drag coefficient.
|
580
|
+
:param CLA: Lift coefficient.
|
581
|
+
:param CSA: Side force coefficient.
|
582
|
+
:param dt: Time step.
|
583
|
+
:param du_insta: Instantaneous velocity difference.
|
584
|
+
:param m_b: Body mass.
|
585
|
+
:param mu: Friction coefficient.
|
586
|
+
:param U_bp: Body velocity array for calculations.
|
587
|
+
:param U_x_dif: X velocity difference.
|
588
|
+
:param U_x_sign: X velocity sign.
|
589
|
+
:param U_y_dif: Y velocity difference.
|
590
|
+
:param U_y_sign: Y velocity sign.
|
591
|
+
:param V_b: Body volume.
|
592
|
+
:param vertical: Vertical position.
|
593
|
+
:return: Body acceleration and velocity arrays.
|
594
|
+
"""
|
595
|
+
|
596
|
+
n_b = len(m_b)
|
597
|
+
half_rho_f = 0.5 * RHO_F
|
598
|
+
zeros_n_b_3 = np.zeros((n_b, 3))
|
599
|
+
|
600
|
+
m_added = (CAM * RHO_F * V_b).T
|
601
|
+
# m_added = ((1+CAM) * RHO_F * V_b).T
|
602
|
+
|
603
|
+
# Initialisation of the forces arrays
|
604
|
+
F_fb = zeros_n_b_3.copy()
|
605
|
+
F_g = zeros_n_b_3.copy()
|
606
|
+
F_fr = zeros_n_b_3.copy()
|
607
|
+
F_A = zeros_n_b_3.copy()
|
608
|
+
|
609
|
+
#Rotation matrix to apply to the hydrodynamic coefficients, see article Delhez et al., 2025 for the detail computation of the resultant matrix
|
610
|
+
angle_rel = np.arctan2(U_y_dif, U_x_dif)
|
611
|
+
angle_rel[(U_y_dif == 0) & (U_x_dif == 0)] = 0
|
612
|
+
|
613
|
+
cos_angle_rel_2 = np.cos(2*angle_rel)
|
614
|
+
sin_angle_rel_2 = np.sin(2*angle_rel)
|
615
|
+
|
616
|
+
#Application of the rotation matrix to the hydrodynamic coefficients. To be way more efficient in time, we calculate the values of the matrix analytically instead of algebrically
|
617
|
+
C = np.zeros((n_b,2,2))
|
618
|
+
C[:,0,0] = (CDA - sin_angle_rel_2*CSA)#*0+CDA
|
619
|
+
C[:,0,1] = (CSA*cos_angle_rel_2)#*0+CSA
|
620
|
+
C[:,1,0] = C[:,0,1]
|
621
|
+
C[:,1,1] = (CDA + sin_angle_rel_2*CSA)#*0+CDA
|
622
|
+
|
623
|
+
# Fluid forces (hydrodynamic forces: drag, side and lift)
|
624
|
+
F_fb[:, 0] = U_x_sign * half_rho_f * C[:,0,0] * U_x_dif**2 + U_y_sign * half_rho_f * C[:,0,1] * U_y_dif**2
|
625
|
+
F_fb[:, 1] = U_y_sign * half_rho_f * C[:,1,1] * U_y_dif**2 + U_x_sign * half_rho_f * C[:,1,0] * U_x_dif**2
|
626
|
+
F_fb[:, 2] = half_rho_f * CLA * (U_x_dif**2 + U_y_dif**2) * vertical
|
627
|
+
|
628
|
+
F_D_z = -np.sign(U_bp[:,2])*half_rho_f*(CDA*2)*U_bp[:,2]**2 * vertical #Consider the vertical drag to be consistent (if we consider the lift, we have to consider the drag)
|
629
|
+
F_fb[:,2] += F_D_z
|
630
|
+
|
631
|
+
# Gravitational forces
|
632
|
+
F_g[:, 2] = (m_b - RHO_F * V_b) * G[2] * vertical
|
633
|
+
|
634
|
+
#Instationnary flow added mass force
|
635
|
+
F_A[:,:2] = m_added[:,None] * du_insta
|
636
|
+
|
637
|
+
Forces_but_friction = F_fb + F_g + F_A #Used to reproduce the threshold of the friction force (as it can't create motion)
|
638
|
+
abs_Forces_but_friction = np.hypot(Forces_but_friction[:, 0], Forces_but_friction[:, 1])
|
639
|
+
sign_U_bp = np.sign(U_bp[:,:2])
|
640
|
+
|
641
|
+
# Friction forces, calculation of value and direction
|
642
|
+
F_fr_tot = mu * np.abs(Forces_but_friction[:,2]) * (Forces_but_friction[:,2]<0) #mu depends on the depth (activating the friction only at the bottom)
|
643
|
+
angle = np.arctan2(U_bp[:,1], U_bp[:,0])
|
644
|
+
angle[(U_bp[:, 1] == 0) & (U_bp[:, 0] == 0)] = np.arctan2(Forces_but_friction[(U_bp[:, 1] == 0) & (U_bp[:, 0] == 0), 1], Forces_but_friction[(U_bp[:, 1] == 0) & (U_bp[:, 0] == 0), 0]) #Force the angle to be 0 also when both speed are -0.0 which gives -pi by convention
|
645
|
+
cos_angle = np.abs(np.cos(angle))# + 1*(angle*180/pi==90)
|
646
|
+
sin_angle = np.abs(np.sin(angle))# + 1*(angle*180/pi==0)
|
647
|
+
pourcentage = np.divide(F_fr_tot, np.where(abs_Forces_but_friction != 0, abs_Forces_but_friction, 1))
|
648
|
+
cos_angle = cos_angle*(pourcentage<=1) + (pourcentage>1)
|
649
|
+
sin_angle = sin_angle*(pourcentage<=1) + (pourcentage>1)
|
650
|
+
F_fr[:, 0] = np.abs(Forces_but_friction[:,0])*pourcentage*cos_angle * -(sign_U_bp[:, 0] + (sign_U_bp[:, 0] == 0) * np.sign(Forces_but_friction[:, 0]))
|
651
|
+
F_fr[:, 1] = np.abs(Forces_but_friction[:,1])*pourcentage*sin_angle * -(sign_U_bp[:, 1] + (sign_U_bp[:, 1] == 0) * np.sign(Forces_but_friction[:, 1]))
|
652
|
+
|
653
|
+
# Newton second law and motion equation
|
654
|
+
acc_b = (Forces_but_friction + F_fr) / (m_b + m_added)[:,None]
|
655
|
+
|
656
|
+
U_b = U_bp + acc_b*dt[:,None]
|
657
|
+
corr_friction = (np.abs(F_fr[:, :2]) < np.abs(Forces_but_friction[:, :2]))
|
658
|
+
U_b[:,:2] *= corr_friction
|
659
|
+
|
660
|
+
return acc_b,U_b
|
661
|
+
|
662
|
+
def Parallel_loop(args):
|
663
|
+
"""
|
664
|
+
Used to run the code in Multiprocess.
|
665
|
+
|
666
|
+
:param args: Arguments for the Loop_management function.
|
667
|
+
:return: Result of the Loop_management function.
|
668
|
+
"""
|
669
|
+
result = Loop_management(*args)
|
670
|
+
return result
|
671
|
+
|
672
|
+
def Preparation_parallelisation(progress_queue, a_RK, BC_cells, count, count_Wolf, CFL, Delta, Human_np, i_initial, n_b, n_saved, n_parallel, n_t, NbX, NbY, Path_saving, Path_Wolf, Pos, Pos_b, resurface, sinking, time, time_b, time_goal, U, U_b, wanted_time, wanted_Wolf, Z_param_np):
|
673
|
+
"""
|
674
|
+
Splits the arrays in the number we want to be ran in MultiProcess.
|
675
|
+
|
676
|
+
:param progress_queue: Queue for progress updates.
|
677
|
+
:param a_RK: Runge-Kutta coefficient.
|
678
|
+
:param BC_cells: Boundary condition cells.
|
679
|
+
:param count: Count of saved states.
|
680
|
+
:param count_Wolf: Count of Wolf states.
|
681
|
+
:param CFL: Courant-Friedrichs-Lewy number.
|
682
|
+
:param Delta: Array of delta values.
|
683
|
+
:param Human_np: Human parameters array.
|
684
|
+
:param i_initial: Initial index.
|
685
|
+
:param n_b: Number of bodies.
|
686
|
+
:param n_saved: Number of saved states.
|
687
|
+
:param n_parallel: Number of parallel processes.
|
688
|
+
:param n_t: Number of time steps.
|
689
|
+
:param NbX: Number of cells in X direction.
|
690
|
+
:param NbY: Number of cells in Y direction.
|
691
|
+
:param Path_saving: Path to save results.
|
692
|
+
:param Path_Wolf: Path to Wolf results.
|
693
|
+
:param Pos: Body position array for calculations.
|
694
|
+
:param Pos_b: Body position array for savings.
|
695
|
+
:param resurface: Resurface array.
|
696
|
+
:param sinking: Sinking array.
|
697
|
+
:param time: Time array for calculations.
|
698
|
+
:param time_b: Time array for savings.
|
699
|
+
:param time_goal: Time goal.
|
700
|
+
:param U: Body velocity array for calculations.
|
701
|
+
:param U_b: Body velocity array for savings.
|
702
|
+
:param wanted_time: Array of wanted times.
|
703
|
+
:param wanted_Wolf: Array of wanted Wolf times.
|
704
|
+
:param Z_param_np: Z parameters array.
|
705
|
+
:return: List of tasks for parallel processing.
|
706
|
+
"""
|
707
|
+
chunk_size = n_b // n_parallel # Taille des sous-ensembles
|
708
|
+
TASKS = []
|
709
|
+
|
710
|
+
if n_parallel > 1:
|
711
|
+
for i in range(n_parallel):
|
712
|
+
start_idx = i * chunk_size
|
713
|
+
end_idx = (i + 1) * chunk_size if i != n_parallel - 1 else n_b # Prend tout jusqu'à la fin au dernier
|
714
|
+
|
715
|
+
# Préparation des arguments pour chaque processus avec sous-vecteurs
|
716
|
+
task = (progress_queue,i,a_RK,BC_cells, count,count_Wolf, CFL, Delta, Human_np[start_idx:end_idx,:], i_initial, chunk_size, n_saved, n_t, NbX, NbY,
|
717
|
+
Path_saving,Path_Wolf, Pos[start_idx:end_idx,:,:], Pos_b[start_idx:end_idx,:,:], resurface[start_idx:end_idx],
|
718
|
+
sinking[start_idx:end_idx,:], time[start_idx:end_idx], time_b[start_idx:end_idx,:], time_goal,
|
719
|
+
U[start_idx:end_idx,:,:], U_b[start_idx:end_idx,:,:], wanted_time, wanted_Wolf, Z_param_np[start_idx:end_idx,:])
|
720
|
+
TASKS.append(task)
|
721
|
+
|
722
|
+
return TASKS
|
723
|
+
|
724
|
+
def Read_Wolf_GPU_mat(i_Wolf, Path_Wolf):
|
725
|
+
"""
|
726
|
+
Reads the results of WolfGPU at a particular time and returns the water depth and velocities' matrix.
|
727
|
+
|
728
|
+
:param i_Wolf: Index of the WolfGPU result.
|
729
|
+
:param Path_Wolf: Path to the WolfGPU results.
|
730
|
+
:return: Water depth and velocities' matrix.
|
731
|
+
"""
|
732
|
+
dir_sim = Path(Path_Wolf)
|
733
|
+
dir_sim_gpu = Path(dir_sim)# / 'simul/simulations/sim_Hydrogram'
|
734
|
+
|
735
|
+
store = ResultsStore(dir_sim_gpu / 'Results', mode='r')
|
736
|
+
|
737
|
+
_,_,_, _, h_store,qx_store,qy_store = store.get_result(i_Wolf)
|
738
|
+
|
739
|
+
h_store = np.where(h_store == 0, 10**-7, h_store)
|
740
|
+
|
741
|
+
Ux = np.nan_to_num(qx_store/h_store, nan=0)
|
742
|
+
|
743
|
+
Uy = np.nan_to_num(qy_store/h_store, nan=0)
|
744
|
+
|
745
|
+
return h_store,Ux,Uy
|
746
|
+
|
747
|
+
def Read_Wolf_GPU_metadata(Path_Wolf):
|
748
|
+
"""
|
749
|
+
Reads the parameters of the WolfGPU simulation and returns the relevant ones.
|
750
|
+
|
751
|
+
:param Path_Wolf: Path to the WolfGPU results.
|
752
|
+
:return: Boundary condition cells, Wolf time step, grid spacing, water depth, number of cells in X and Y directions, and total simulation time.
|
753
|
+
"""
|
754
|
+
dir_sim = Path(Path_Wolf)
|
755
|
+
dir_sim_gpu = Path(dir_sim)# / 'simul/simulations/sim_Hydrogram'
|
756
|
+
|
757
|
+
with open(dir_sim_gpu / 'parameters.json','r') as file:
|
758
|
+
parameters = json.load(file)
|
759
|
+
|
760
|
+
param = parameters["parameters"]
|
761
|
+
DX = param["dx"]
|
762
|
+
DY = param["dy"]
|
763
|
+
NbX = param["nx"]
|
764
|
+
NbY = param["ny"]
|
765
|
+
|
766
|
+
dur = param["duration"]
|
767
|
+
t_tot = dur["duration"]
|
768
|
+
|
769
|
+
dur_dt = param["report_period"]
|
770
|
+
dt_WOLF = dur_dt["duration"]
|
771
|
+
|
772
|
+
BC = parameters["boundary_conditions"]
|
773
|
+
|
774
|
+
BC_cells = np.array([(bc['i'], bc['j']) for bc in BC])
|
775
|
+
|
776
|
+
store = ResultsStore(dir_sim_gpu / 'Results', mode='r')
|
777
|
+
|
778
|
+
_,_,_, _, h_store,_,_ = store.get_result(1)
|
779
|
+
|
780
|
+
return BC_cells,dt_WOLF,DX,DY,h_store,NbX,NbY,t_tot
|
781
|
+
|
782
|
+
def Save_wanted_time(Path, Pos_b, U_b, count):
|
783
|
+
"""
|
784
|
+
Saves the body positions and velocities at a given time.
|
785
|
+
|
786
|
+
:param Path: Path to save the results.
|
787
|
+
:param Pos_b: Body position array for savings.
|
788
|
+
:param U_b: Body velocity array for savings.
|
789
|
+
:param count: Count of saved states.
|
790
|
+
"""
|
791
|
+
Path_Pos = os.path.join(Path,f'Pos_{count:04d}')
|
792
|
+
Path_U = os.path.join(Path,f'U_{count:04d}')
|
793
|
+
np.savez(Path_Pos,Pos_b=Pos_b)
|
794
|
+
np.savez(Path_U,U=U_b)
|
795
|
+
|
796
|
+
def Skinfold(n_b, known, Human):
|
797
|
+
"""
|
798
|
+
Determines the body density based on its age and BMI using Siri's equation. An error percentage body fat is also set, according to Meeuwsen et al., 2010.
|
799
|
+
|
800
|
+
:param n_b: Number of bodies.
|
801
|
+
:param known: Known parameter.
|
802
|
+
:param Human: Human parameters array.
|
803
|
+
:return: Updated Human parameters and error percentage body fat.
|
804
|
+
"""
|
805
|
+
std_perc_fat_m = [8.1, 7.6, 7.0, 6.4, 6.2, 6.7, 6.8] #Meeuwsen et al
|
806
|
+
std_perc_fat_w = [8, 8.1, 8.4, 8.4, 8.4, 8.3, 8.3, 9.4]
|
807
|
+
error_perc_fat = np.zeros((n_b))
|
808
|
+
|
809
|
+
ind_20_m = np.where((Human.Age.to_numpy()<=20) & (Human.gender.to_numpy()==1))
|
810
|
+
ind_30_m = np.where((Human.Age.to_numpy()<=30)&(Human.Age.to_numpy()>20) & (Human.gender.to_numpy()==1))
|
811
|
+
ind_40_m = np.where((Human.Age.to_numpy()<=40)&(Human.Age.to_numpy()>30) & (Human.gender.to_numpy()==1))
|
812
|
+
ind_50_m = np.where((Human.Age.to_numpy()<=50)&(Human.Age.to_numpy()>40) & (Human.gender.to_numpy()==1))
|
813
|
+
ind_max_m = np.where((Human.Age.to_numpy()>50) & (Human.gender.to_numpy() ==1))
|
814
|
+
|
815
|
+
ind_20_w = np.where((Human.Age.to_numpy()<=20) & (Human.gender.to_numpy()==2))
|
816
|
+
ind_30_w = np.where((Human.Age.to_numpy()<=30)&(Human.Age.to_numpy()>20) & (Human.gender.to_numpy()==2))
|
817
|
+
ind_40_w = np.where((Human.Age.to_numpy()<=40)&(Human.Age.to_numpy()>30) & (Human.gender.to_numpy()==2))
|
818
|
+
ind_50_w = np.where((Human.Age.to_numpy()<=50)&(Human.Age.to_numpy()>40) & (Human.gender.to_numpy()==2))
|
819
|
+
ind_max_w = np.where((Human.Age.to_numpy()>50) & (Human.gender.to_numpy() ==2))
|
820
|
+
|
821
|
+
ind_20 = np.where(Human.Age.to_numpy()<=24)
|
822
|
+
ind_30 = np.where((Human.Age.to_numpy()<=34)&(Human.Age.to_numpy()>24))
|
823
|
+
ind_40 = np.where((Human.Age.to_numpy()<=44)&(Human.Age.to_numpy()>34))
|
824
|
+
ind_50 = np.where((Human.Age.to_numpy()<=54)&(Human.Age.to_numpy()>44))
|
825
|
+
ind_60 = np.where((Human.Age.to_numpy()<=64)&(Human.Age.to_numpy()>54))
|
826
|
+
ind_70 = np.where((Human.Age.to_numpy()<=74)&(Human.Age.to_numpy()>64))
|
827
|
+
ind_max = np.where(Human.Age.to_numpy()>74)
|
828
|
+
|
829
|
+
BMI_25 = [20, 21.3, 22.5, 23.3, 22.9, 23.7, 23.1]
|
830
|
+
BMI_50 = [21.7, 23.4, 24.8, 25.7, 25.9, 26.3, 25.3]
|
831
|
+
BMI_75 = [24.3, 26.4, 28, 29, 29.1, 29.7, 28]
|
832
|
+
|
833
|
+
|
834
|
+
change = [ind_20,ind_30,ind_40,ind_50,ind_60,ind_70,ind_max]
|
835
|
+
|
836
|
+
ind_Age = np.minimum(math.floor(np.mean(Human.Age.to_numpy())/10)-1,6)
|
837
|
+
|
838
|
+
if known == 0:
|
839
|
+
|
840
|
+
for i in range(7):
|
841
|
+
ind = np.array((change[i]))
|
842
|
+
(aBMI,bBMI) = known_1(3,16,40,BMI_25[i],BMI_50[i],0.25,0.5)
|
843
|
+
Human.loc[ind[0,:],'BMI'] = rnd.beta(aBMI,bBMI,size=(len(ind[:][0])))*(40-16)+16
|
844
|
+
|
845
|
+
error_perc_fat = (rnd.beta(2,2,size=(n_b))*(2*std_perc_fat_m[ind_Age]+2*std_perc_fat_m[ind_Age])-2*std_perc_fat_m[ind_Age])*(Human.gender.to_numpy()==1) + (rnd.beta(2,2,size=(n_b))*(2*std_perc_fat_w[ind_Age]+2*std_perc_fat_w[ind_Age])-2*std_perc_fat_w[ind_Age])*(Human.gender.to_numpy()==2)
|
846
|
+
|
847
|
+
Human.loc[:,'mass'] = Human.BMI * Human.height**2
|
848
|
+
|
849
|
+
perc_fat = -32.515 + 12.409*(Human.gender.to_numpy()-1) + 3.306*Human.BMI.to_numpy() - 0.03*Human.BMI.to_numpy()**2 - 0.006*Human.Age.to_numpy() + 0.033*Human.Age.to_numpy()*(Human.gender.to_numpy()-1) - 0.001*Human.Age.to_numpy()*Human.BMI.to_numpy() #Meeuwsen et al
|
850
|
+
perc_fat = perc_fat*(perc_fat+error_perc_fat<45) + (perc_fat-(perc_fat+error_perc_fat-45-random.randint(-100,200))/100)*(perc_fat+error_perc_fat>=45)
|
851
|
+
perc_fat = np.maximum(perc_fat+error_perc_fat,8)
|
852
|
+
Human.rho = 4.95/(perc_fat/100+4.5) * 1000#Siri's equation
|
853
|
+
Human.Volume = Human.mass/Human.rho
|
854
|
+
|
855
|
+
else: #if known != 3:
|
856
|
+
|
857
|
+
ind_Age = np.minimum(math.floor(np.mean(Human.Age.to_numpy())/10)-1,6)
|
858
|
+
|
859
|
+
error_perc_fat = rnd.beta(2,2,size=(n_b))*(2*std_perc_fat_m[ind_Age]+2*std_perc_fat_m[ind_Age])-(2*std_perc_fat_m[ind_Age])*(Human.gender.to_numpy()==1) + (rnd.beta(2,2,size=(n_b))*(2*std_perc_fat_w[ind_Age]+2*std_perc_fat_w[ind_Age])-2*std_perc_fat_w[ind_Age])*(Human.gender.to_numpy()==2)
|
860
|
+
|
861
|
+
perc_fat = -32.515 + 12.409*(Human.gender.to_numpy()-1) + 3.306*Human.BMI.to_numpy() - 0.03*Human.BMI.to_numpy()**2 - 0.006*Human.Age.to_numpy() + 0.033*Human.Age.to_numpy()*(Human.gender.to_numpy()-1) - 0.001*Human.Age.to_numpy()*Human.BMI.to_numpy() #Meeuwsen et al
|
862
|
+
perc_fat = perc_fat*(perc_fat+error_perc_fat<45) + (perc_fat-(perc_fat+error_perc_fat-45-random.randint(-100,200))/100)*(perc_fat+error_perc_fat>=45)
|
863
|
+
perc_fat = np.maximum(perc_fat+error_perc_fat,8)
|
864
|
+
Human.rho = 4.95/(perc_fat/100+4.5) * 1000;#Siri's equation
|
865
|
+
Human.Volume = Human.mass.to_numpy()/Human.rho.to_numpy()
|
866
|
+
|
867
|
+
return Human,error_perc_fat
|
868
|
+
|
869
|
+
def state_of_run(progress_queue, frame, interval):
|
870
|
+
"""
|
871
|
+
Monitoring function that displays progress every `interval` seconds.
|
872
|
+
|
873
|
+
:param progress_queue: Queue for progress updates.
|
874
|
+
:param frame: Frame object for GUI updates.
|
875
|
+
:param interval: Time interval for updates.
|
876
|
+
"""
|
877
|
+
progress_dict = {i: None for i in range(frame.n_processes)} # Initialize the progress dictionary
|
878
|
+
while True:
|
879
|
+
# Get the progress updates from the queue
|
880
|
+
try:
|
881
|
+
progress_update = progress_queue.get(timeout=interval) # Timeout after interval second if no updates
|
882
|
+
process_id, progress = progress_update
|
883
|
+
progress_dict[process_id] = progress
|
884
|
+
wx.CallAfter(frame.update_progress,progress_dict) # Update GUI safely from a thread
|
885
|
+
except queue.Empty:
|
886
|
+
continue # Pas de mise à jour dispo, on continue
|
887
|
+
except (EOFError, BrokenPipeError):
|
888
|
+
break
|
889
|
+
|
890
|
+
# Close window when all processes are done
|
891
|
+
# if all(value is not None for value in progress_dict.values()):
|
892
|
+
# wx.CallAfter(frame.Close) # Close the frame after all tasks are complete
|
893
|
+
# break
|
894
|
+
|
895
|
+
def U_turbulence(batch, dt, epsilon, H, k, turb_type, U_x, U_x_dif, U_y, U_y_dif, z, z_0):
|
896
|
+
"""
|
897
|
+
Adjust the flow velocity with turbulence. 4 different evaluations are proposed but each uses a log law of the wall to go from depth-averaged velocity to velocity related to the body vertical position.
|
898
|
+
|
899
|
+
:param batch: Batch turbulence.
|
900
|
+
:param dt: Time step.
|
901
|
+
:param epsilon: Epsilon value.
|
902
|
+
:param H: Water depth.
|
903
|
+
:param k: Turbulence kinetic energy.
|
904
|
+
:param turb_type: Turbulence type.
|
905
|
+
:param U_x: X velocity.
|
906
|
+
:param U_x_dif: X velocity difference.
|
907
|
+
:param U_y: Y velocity.
|
908
|
+
:param U_y_dif: Y velocity difference.
|
909
|
+
:param z: Depth array.
|
910
|
+
:param z_0: Roughness length.
|
911
|
+
:return: Adjusted X and Y velocities.
|
912
|
+
"""
|
913
|
+
n = 0.027 #To be taken from WOLF
|
914
|
+
kappa = 0.41 #Von Karman constant
|
915
|
+
|
916
|
+
U_shear_x = np.zeros_like(H)
|
917
|
+
U_shear_y = np.zeros_like(H)
|
918
|
+
U_x_sign = np.zeros_like(H)
|
919
|
+
U_y_sign = np.zeros_like(H)
|
920
|
+
ln_z_z0 = np.zeros_like(H)
|
921
|
+
|
922
|
+
mask_zero = (H >= 0) & (z >= 0)
|
923
|
+
|
924
|
+
U_shear_x[mask_zero] = np.abs(U_x[mask_zero])*n*np.sqrt(9.81)/(H[mask_zero]**(1/6)) #based on the combination of Manning equation and definition of bed shear stress and shear velocity
|
925
|
+
U_shear_y[mask_zero] = np.abs(U_y[mask_zero])*n*np.sqrt(9.81)/(H[mask_zero]**(1/6))
|
926
|
+
|
927
|
+
U_x_sign[mask_zero] = np.sign(U_x[mask_zero])
|
928
|
+
U_y_sign[mask_zero] = np.sign(U_y[mask_zero])
|
929
|
+
|
930
|
+
ln_z_z0[mask_zero] = np.log(z[mask_zero] / z_0)
|
931
|
+
|
932
|
+
if turb_type == 0: #No turbulence
|
933
|
+
|
934
|
+
ln_H_z0 = np.log(H / z_0)
|
935
|
+
factor = ln_H_z0# + z / H - 1 #see Eq. 7.2.12 of Principles of sediment transport in rivers, estuaries and coastal seas (Leo van Rijn)
|
936
|
+
|
937
|
+
U_x = (U_x / factor) * ln_z_z0
|
938
|
+
U_y = (U_y / factor) * ln_z_z0
|
939
|
+
|
940
|
+
elif turb_type==1: #Based on a random parameter
|
941
|
+
|
942
|
+
percentage = 5 *10**-2
|
943
|
+
|
944
|
+
n_b = len(U_x)
|
945
|
+
|
946
|
+
index_x = np.random.randint(0,len(batch), n_b) #Trick to not make a rnd.normal at each iteration (faster)
|
947
|
+
index_y = np.roll(index_x,-1) #Trick to avoid using two randint
|
948
|
+
# index = np.random.choice(len(batch), n_b) # Numba
|
949
|
+
R_x = batch[index_x] #Number choosen randomly from a gaussian distribution of mean 0 and std 1
|
950
|
+
R_y = batch[index_y]
|
951
|
+
|
952
|
+
U_xy = np.hypot(U_shear_x,U_shear_y)/kappa * ln_z_z0
|
953
|
+
|
954
|
+
U_x = U_shear_x/ kappa * ln_z_z0 #Formula derived from the log velocity profile used in boundary layer theory for turbulent open-channel flow
|
955
|
+
U_x_turb = R_x*percentage*U_x
|
956
|
+
U_x = U_x_sign*U_x + U_x_turb
|
957
|
+
|
958
|
+
U_y = U_shear_y/ kappa * ln_z_z0
|
959
|
+
U_y_turb = R_y*percentage*U_y
|
960
|
+
U_y = U_y_sign*U_y + U_y_turb
|
961
|
+
|
962
|
+
elif turb_type==2: #See Garcia et al. (2013), page 214, based on the shear velocity, I think this is false as it is applied on floating particles and on the position itself, can't work applied on the velocity and with friction with the bottom
|
963
|
+
|
964
|
+
n_b = len(U_x)
|
965
|
+
|
966
|
+
index = np.random.randint(0,len(batch), n_b) #Trick to not make a rnd.normal at each iteration
|
967
|
+
# index = np.random.choice(len(batch), n_b) # Numba
|
968
|
+
R = batch[index] #Constant used in the definition of U_prime
|
969
|
+
|
970
|
+
#U_shear_glob = np.sqrt(U_x**2+U_y**2)*n*np.sqrt(9.81)/(H**(1/6))
|
971
|
+
|
972
|
+
|
973
|
+
#U_x_shear = (U_x / factor) * ln_z_z0 #Log law of the wall
|
974
|
+
K_H = 0.6*H*np.abs(U_shear_x)#np.abs(U_x_shear) #Turbulent diffusion coefficient (or turbulent diffusivity coefficient)
|
975
|
+
U_x_turb = R*np.sqrt(2*K_H*dt) #In the original formula (Garcia et al. (2013), Eq. 6), we have R*np.sqrt(2*K_H*dt) but as we multiply by dt after that, we have to divide by dt before
|
976
|
+
U_x = U_shear_x/ kappa * ln_z_z0 + U_x_turb/dt
|
977
|
+
|
978
|
+
#U_y_shear = (U_y / factor) * ln_z_z0 #Log law of the wall
|
979
|
+
K_H = 0.6*H*np.abs(U_shear_y)#np.abs(U_y_shear) #Turbulent diffusion coefficient (or turbulent diffusivity coefficient)
|
980
|
+
U_y_turb = R*np.sqrt(2*K_H*dt)
|
981
|
+
U_y = U_shear_y/ kappa * ln_z_z0 + U_y_turb/dt
|
982
|
+
|
983
|
+
elif turb_type ==3: #See Bocksell & Loth (2001), Eqs. 16 to 21, based on k-eps
|
984
|
+
|
985
|
+
n_b = len(U_x)
|
986
|
+
epsilon = np.maximum(epsilon,10**-10) #Because the correction of collision is done after, trick to avoid to divide by 0 when out of the domain
|
987
|
+
|
988
|
+
index = np.random.randint(0,len(batch), n_b) #Trick to not make a rnd.normal at each iteration
|
989
|
+
# index = np.random.choice(len(batch), n_b) # Numba
|
990
|
+
R = batch[index] #Same as psi in the reference
|
991
|
+
eps_01_09 = index/n_b/10*8+0.1 #Explanation just after Eq. 17
|
992
|
+
|
993
|
+
C_c_x = 1 + np.sign(U_x_dif) #Eq. 17
|
994
|
+
C_c_y = 1 + np.sign(U_y_dif) #Eq. 17
|
995
|
+
C_delta = 1.6 #Table 2 of the reference
|
996
|
+
C_mu = 0.09 #After Eq. 17 of reference
|
997
|
+
C_tau = 0.27 #Table 2 of the reference
|
998
|
+
|
999
|
+
Delta = np.array([C_c_x,C_c_y])*C_delta*C_mu**(3/4)*(k**(3/2)/epsilon) #Eq. 17
|
1000
|
+
|
1001
|
+
tau_delta = C_tau*eps_01_09*(k/epsilon) #Eq. 17
|
1002
|
+
tau_t_abs = np.abs(Delta/np.array([U_x_dif,U_y_dif])) #Eq. 17
|
1003
|
+
tau_t_sign = np.sign(Delta/np.array([U_x_dif,U_y_dif])) #Eq. 17
|
1004
|
+
tau_t_sign[tau_t_sign==0]=1
|
1005
|
+
tau_int = np.maximum(np.maximum(tau_delta,tau_t_abs),10**-10) #Eq. 16, problem, if we keep min, we have 0 and so U_y = inf, we test with max
|
1006
|
+
|
1007
|
+
alpha = np.exp(-dt/tau_int)*tau_t_sign #Eq. 20
|
1008
|
+
sigma_u = np.sqrt(2/3*k) #Eq. 20
|
1009
|
+
|
1010
|
+
U_x = alpha[0,:]*U_x + np.sqrt((1-alpha[0,:]**2)*sigma_u**2)*R# + U_x #Eq. 18
|
1011
|
+
U_y = alpha[1,:]*U_y + np.sqrt((1-alpha[1,:]**2)*sigma_u**2)*R# + U_y #Eq. 18
|
1012
|
+
|
1013
|
+
##At this time, compatible only with Wolf CPU because no k-eps given by GPU
|
1014
|
+
|
1015
|
+
return U_x,U_y
|
1016
|
+
|
1017
|
+
|
1018
|
+
|
1019
|
+
|