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,2124 @@
|
|
1
|
+
import numpy as np
|
2
|
+
import pandas as pd
|
3
|
+
import matplotlib.pyplot as plt
|
4
|
+
from matplotlib.widgets import Slider
|
5
|
+
from numpy import random as rnd
|
6
|
+
import math
|
7
|
+
from pathlib import Path
|
8
|
+
import json
|
9
|
+
import timeit
|
10
|
+
import multiprocessing
|
11
|
+
import cProfile, pstats
|
12
|
+
import wx
|
13
|
+
from os.path import join
|
14
|
+
import threading
|
15
|
+
from sklearn.neighbors import KernelDensity
|
16
|
+
from scipy.ndimage import maximum_filter
|
17
|
+
from scipy.spatial.distance import cdist
|
18
|
+
from datetime import datetime, timedelta
|
19
|
+
|
20
|
+
try:
|
21
|
+
from ..PyParams import *
|
22
|
+
from ..drawing_obj import Element_To_Draw
|
23
|
+
from ..Results2DGPU import getkeyblock
|
24
|
+
from ..PyTranslate import _
|
25
|
+
from ..PyVertex import cloud_vertices
|
26
|
+
from ..wolf_array import WolfArray, header_wolf
|
27
|
+
from ..PandasGrid import PandasGrid
|
28
|
+
except:
|
29
|
+
from wolfhece.PyParams import *
|
30
|
+
from wolfhece.drawing_obj import Element_To_Draw
|
31
|
+
from wolfhece.Results2DGPU import getkeyblock
|
32
|
+
from wolfhece.PyTranslate import _
|
33
|
+
from wolfhece.PyVertex import cloud_vertices
|
34
|
+
from wolfhece.wolf_array import WolfArray
|
35
|
+
from wolfhece.PandasGrid import PandasGrid
|
36
|
+
|
37
|
+
try:
|
38
|
+
from .drowning_functions import *
|
39
|
+
except:
|
40
|
+
from wolfhece.drowning_victims.drowning_functions import *
|
41
|
+
|
42
|
+
|
43
|
+
#index 0 1 2 3 4 5 6 7
|
44
|
+
COLUMN_Z_PARAM = ['vertical','U_z','z_0','mu_stat','Time_float','T_w','ADD','ADD_resurface']
|
45
|
+
COLUMNS_HUMAN = ['Age','BMI','BSA','CAM','CDA','CLA','Death','dm','eps','fp_x','fp_y','fp_z','gender','height','lungs_volume_FRC','lungs_volume_TLC','mass','rho','Volume','V_clothes_o','V_clothes_one','V_clothes_two','error_perc_fat','CSA']
|
46
|
+
|
47
|
+
class Drowning_victim:
|
48
|
+
def __init__(self,Path_dir:str = None):
|
49
|
+
"""
|
50
|
+
Initialize the simulation parameters.
|
51
|
+
|
52
|
+
:param Path_loading:
|
53
|
+
Path of the simulation loaded.
|
54
|
+
|
55
|
+
Attributes:
|
56
|
+
Profile_this (bool): Binary parameter to activate the profiling of the code.
|
57
|
+
saving (bool): Binary parameter to save your results.
|
58
|
+
file_name (str): Name of the file to be saved.
|
59
|
+
Path_saving (str): Path where you want the file saved.
|
60
|
+
loading (bool): Binary parameter to load previous results and start from them.
|
61
|
+
Path_loading (str): Path of the simulation loaded.
|
62
|
+
Path_Wolf (str): Path of the WolfGPU simulation.
|
63
|
+
plot_pos (bool): Binary parameter to plot your results.
|
64
|
+
CFL (float): CFL number to calculate the time step of your simulation.
|
65
|
+
dt_min (float): Minimum time step for your variable time step.
|
66
|
+
dt_max (float): Maximum time step for your variable time step.
|
67
|
+
t_initial (float): Initial time of the simulation.
|
68
|
+
Days (int): Number of days of the simulation.
|
69
|
+
Hours (int): Number of hours of the simulation.
|
70
|
+
Minutes (int): Number of minutes of the simulation.
|
71
|
+
Seconds (int): Number of seconds of the simulation.
|
72
|
+
wanted_time (list): Array with all the times at which we want a save.
|
73
|
+
n_t (int): Length of wanted_time.
|
74
|
+
count_initial (int): Initial step of the simulation.
|
75
|
+
count_pre (int): Initial step of the simulation - 1.
|
76
|
+
n_b (int): Number of simulated bodies.
|
77
|
+
n_parallel (int): Number of times the process is parallelized (number of cores used).
|
78
|
+
random_IP (float): Radius of the uncertainty area of the drowning point (in cells).
|
79
|
+
T_water (float): Average water temperature in °C.
|
80
|
+
vertical (bool): Binary parameter to consider vertical motion.
|
81
|
+
DZ (float): Step size for vertical motion (used in simulation).
|
82
|
+
Z_param (pd.DataFrame): Dataframe holding the parameters for vertical motion simulation.
|
83
|
+
"""
|
84
|
+
|
85
|
+
self.Default_values()
|
86
|
+
self.from_attributes_to_dictionnary()
|
87
|
+
|
88
|
+
## Loads all the parameters from the parameters.param file
|
89
|
+
if Path_dir is not None:
|
90
|
+
self.Path_saving = Path(Path_dir)
|
91
|
+
self.from_dot_param_to_dictionnary(store_dir=self.Path_saving)
|
92
|
+
self.from_dictionnary_to_attributes()
|
93
|
+
# self.update_params(str(self.Path_saving))
|
94
|
+
|
95
|
+
def Default_values(self):
|
96
|
+
"""
|
97
|
+
Sets the default values for each parameter by creating a first parameter.param
|
98
|
+
"""
|
99
|
+
import pandas as pd
|
100
|
+
import math
|
101
|
+
import numpy.random as rnd
|
102
|
+
|
103
|
+
self.Profile_this = 0
|
104
|
+
self.Redraw = 0#[1,2,3] #0 for no
|
105
|
+
|
106
|
+
current_dir = Path(__file__).resolve().parent
|
107
|
+
self.current_dir = current_dir
|
108
|
+
self.saving = 1
|
109
|
+
self.file_name = 'Test'
|
110
|
+
|
111
|
+
self.loading = 0
|
112
|
+
self.Path_loading = None
|
113
|
+
self.Path_saving = None
|
114
|
+
|
115
|
+
self.Path_Wolf = None
|
116
|
+
|
117
|
+
self.plot_pos = 0
|
118
|
+
|
119
|
+
self.a_RK = 0.5
|
120
|
+
|
121
|
+
self.image = 0
|
122
|
+
|
123
|
+
self.CFL = 0.01
|
124
|
+
self.dt_min = 0.01
|
125
|
+
self.dt_max = 1 #s
|
126
|
+
self.t_initial = 0*60*60*24
|
127
|
+
self.i_initial = math.floor(self.t_initial/self.dt_max)+1
|
128
|
+
|
129
|
+
self.Days = int(0) #days
|
130
|
+
self.Hours = int(1) #h
|
131
|
+
self.Minutes = int(0) #min
|
132
|
+
self.Seconds = int(0) #s
|
133
|
+
time_goal = self.Days*24*60*60 + self.Hours*60*60 + self.Minutes*60 + self.Seconds #s
|
134
|
+
self.time_goal = time_goal
|
135
|
+
|
136
|
+
self.ind_pos_0_x = 0 #For L14: 3983, L_30: 4303
|
137
|
+
self.ind_pos_0_y = 0 #For L14: 3780, L_30: 3902
|
138
|
+
|
139
|
+
self.origx = 215702
|
140
|
+
self.origy = 130000
|
141
|
+
self.dx = 5
|
142
|
+
self.dy = 5
|
143
|
+
self.nbx = 8460
|
144
|
+
self.nby = 10416
|
145
|
+
|
146
|
+
self.n_saved = 1
|
147
|
+
|
148
|
+
self.n_b = 10000
|
149
|
+
n_b = self.n_b
|
150
|
+
self.n_parallel = 2 #Number of processes to be ran in parallel
|
151
|
+
Z_param = pd.DataFrame(data=None,columns=COLUMN_Z_PARAM,dtype=np.int32)
|
152
|
+
Z_param.vertical = 1 # 1 = Consider the vertical motion, 0 = not considered
|
153
|
+
Z_param.U_z = 0*np.ones((n_b)) #0 = U constant on depth, 1 = U varies with the depth (log law)
|
154
|
+
d_50 = 2*2*40 *10**-3 #to be confirmed
|
155
|
+
Z_param.z_0 = d_50/30*np.ones((n_b)) #experimental results of Nikuradse (not found if published in 1933 or 1950 but nobody seems to care)
|
156
|
+
Z_param.mu_stat = 1 * np.ones((n_b)) #rnd.beta(1,1,size=(n_b))*(1-0.3)+0.3
|
157
|
+
Z_param.Time_float = 0*np.ones((n_b))
|
158
|
+
Z_param.T_w = 15*np.ones((n_b))
|
159
|
+
Z_param.ADD = time_goal/60/60/24*15
|
160
|
+
Z_param.ADD_resurface = 5250/15 * rnd.beta(4,4,size=n_b) #source: Heaton 2011 considering a TADS between 14 and 15 as maximum expension
|
161
|
+
self.Z_param = Z_param
|
162
|
+
|
163
|
+
## Let the viewer edit the parameters
|
164
|
+
self.victim()
|
165
|
+
|
166
|
+
self.path = Path(current_dir)
|
167
|
+
# self.save_json(Path(self.Path_saving))
|
168
|
+
|
169
|
+
def victim(self):
|
170
|
+
"""
|
171
|
+
Definition of the victim's caracteristics
|
172
|
+
|
173
|
+
gender : Gender of the victim, 1 for man, 2 for women
|
174
|
+
Age : Age of the victim in years
|
175
|
+
height : Height of the victim in m
|
176
|
+
mass : Mass of the victim in kg
|
177
|
+
BMI : BMI of the victim in kg/m²
|
178
|
+
clothes : clothing type of the victim (0 for naked, 1 for summer clothes, 2 for spring clothes, 3 for winter clothes)
|
179
|
+
T_w : Average water temperature in °C
|
180
|
+
ini_drowning : Time at which the victim drowned in the day (format 24H)
|
181
|
+
|
182
|
+
"""
|
183
|
+
self.gender = -1
|
184
|
+
self.Age = -1
|
185
|
+
self.height = -1
|
186
|
+
self.mass = -1
|
187
|
+
self.BMI = -1
|
188
|
+
self.clothes = -1
|
189
|
+
self.T_w = 15
|
190
|
+
self.ini_drowning = 10 #simpledialog.askinteger('Hour at which the victim fell in the water','Time of drowning in hours: \nExample: 2 AM (2h00) being 2 \n5 PM (17h00) being 17',minvalue=0,maxvalue=23,parent=root)
|
191
|
+
self.m_b_add = 0 #mass of added accessories
|
192
|
+
|
193
|
+
def from_attributes_to_dictionnary(self):
|
194
|
+
"Create a dictionnary from the attributes of the class"
|
195
|
+
|
196
|
+
param_dict = {}
|
197
|
+
|
198
|
+
# Dictionnaire des sections et paramètres à ajouter
|
199
|
+
param_dict = {
|
200
|
+
"Options": {
|
201
|
+
"Profile": {
|
202
|
+
"value": self.Profile_this,
|
203
|
+
"explicit name": "Profile",
|
204
|
+
"description": "Do you want to profile your code?",
|
205
|
+
"type": "Integer",
|
206
|
+
"choices": {
|
207
|
+
"Don't profile the code":0,
|
208
|
+
"Profile the code":1
|
209
|
+
},
|
210
|
+
"mandatory": False
|
211
|
+
},
|
212
|
+
"Save": {
|
213
|
+
"value": self.saving,
|
214
|
+
"explicit name": "Save",
|
215
|
+
"description": "Enable saving of results?",
|
216
|
+
"type": "Integer",
|
217
|
+
"choices": {
|
218
|
+
"Don't save":0,
|
219
|
+
"Save the results":1
|
220
|
+
},
|
221
|
+
"mandatory": True
|
222
|
+
},
|
223
|
+
"Load": {
|
224
|
+
"value": self.loading,
|
225
|
+
"explicit name": "Load",
|
226
|
+
"description": "Enable loading of previous results?",
|
227
|
+
"type": "Integer",
|
228
|
+
"choices": {
|
229
|
+
"Don't load the results from a previous simulation":0,
|
230
|
+
"Load":1
|
231
|
+
},
|
232
|
+
"mandatory": True
|
233
|
+
},
|
234
|
+
"Plot": {
|
235
|
+
"value": self.plot_pos,
|
236
|
+
"explicit name": "Plot",
|
237
|
+
"description": "Enable plotting of results?",
|
238
|
+
"type": "Integer",
|
239
|
+
"choices": {
|
240
|
+
"Don't plot":0,
|
241
|
+
"Plot the results when simulation is over":1
|
242
|
+
},
|
243
|
+
"mandatory": False
|
244
|
+
},
|
245
|
+
"n_parallel": {
|
246
|
+
"value": self.n_parallel,
|
247
|
+
"explicit name": "Number of parallel processes",
|
248
|
+
"description": "Number of parallel processes to use",
|
249
|
+
"type": "Integer",
|
250
|
+
"choices": None,
|
251
|
+
"mandatory": True
|
252
|
+
},
|
253
|
+
"vertical": {
|
254
|
+
"value": 1, # Assuming vertical motion is always enabled
|
255
|
+
"explicit name": "Vertical motion",
|
256
|
+
"description": "Consider vertical motion in the simulation?",
|
257
|
+
"type": "Integer",
|
258
|
+
"choices": {
|
259
|
+
"No vertical motion allowed":0,
|
260
|
+
"With vertical motion allowed":1
|
261
|
+
},
|
262
|
+
"mandatory": False
|
263
|
+
},
|
264
|
+
"a_RK": {
|
265
|
+
"value": self.a_RK,
|
266
|
+
"explicit name": "Runge-Kutta ponderation coefficient",
|
267
|
+
"description": "Coefficient for RK22 integration",
|
268
|
+
"type": "Float",
|
269
|
+
"choices": None,
|
270
|
+
"mandatory": False
|
271
|
+
},
|
272
|
+
"image": {
|
273
|
+
"value": self.image,
|
274
|
+
"explicit name": "Progression bar",
|
275
|
+
"description": "Enable image generation",
|
276
|
+
"type": "Integer",
|
277
|
+
"choices": {
|
278
|
+
"Plot progress with loading bar":0,
|
279
|
+
"Plot progress with progress image":1
|
280
|
+
},
|
281
|
+
"mandatory": False
|
282
|
+
}
|
283
|
+
},
|
284
|
+
"Paths": {
|
285
|
+
"File": {
|
286
|
+
"value": self.file_name,
|
287
|
+
"explicit name": "File name",
|
288
|
+
"description": "Name of the file to save",
|
289
|
+
"type": "String",
|
290
|
+
"choices": None,
|
291
|
+
"mandatory": True
|
292
|
+
},
|
293
|
+
"Save": {
|
294
|
+
"value": self.Path_saving,
|
295
|
+
"explicit name": "Save path",
|
296
|
+
"description": "Path where results will be saved",
|
297
|
+
"type": "Directory",
|
298
|
+
"choices": None,
|
299
|
+
"mandatory": True
|
300
|
+
},
|
301
|
+
"Load": {
|
302
|
+
"value": self.Path_loading,
|
303
|
+
"explicit name": "Load path",
|
304
|
+
"description": "Path to load previous results",
|
305
|
+
"type": "Directory",
|
306
|
+
"choices": None,
|
307
|
+
"mandatory": False
|
308
|
+
},
|
309
|
+
"Wolf": {
|
310
|
+
"value": self.Path_Wolf,
|
311
|
+
"explicit name": "Results of Wolf GPU simulation path",
|
312
|
+
"description": "Path to the WolfGPU simulation",
|
313
|
+
"type": "Directory",
|
314
|
+
"choices": None,
|
315
|
+
"mandatory": True
|
316
|
+
}
|
317
|
+
},
|
318
|
+
"DT": {
|
319
|
+
"CFL": {
|
320
|
+
"value": self.CFL,
|
321
|
+
"explicit name": "CFL",
|
322
|
+
"description": "Courant number for time step calculation",
|
323
|
+
"type": "Float",
|
324
|
+
"choices": None,
|
325
|
+
"mandatory": False
|
326
|
+
},
|
327
|
+
"dt_min": {
|
328
|
+
"value": self.dt_min,
|
329
|
+
"explicit name": "Minimum time step",
|
330
|
+
"description": "Minimum time step in seconds",
|
331
|
+
"type": "Float",
|
332
|
+
"choices": None,
|
333
|
+
"mandatory": False
|
334
|
+
},
|
335
|
+
"dt_max": {
|
336
|
+
"value": self.dt_max,
|
337
|
+
"explicit name": "Maximum time step",
|
338
|
+
"description": "Maximum time step in seconds",
|
339
|
+
"type": "Float",
|
340
|
+
"choices": None,
|
341
|
+
"mandatory": False
|
342
|
+
}
|
343
|
+
},
|
344
|
+
"Duration": {
|
345
|
+
"t_d": {
|
346
|
+
"value": self.Days,
|
347
|
+
"explicit name": "Days",
|
348
|
+
"description": "Number of days for the simulation",
|
349
|
+
"type": "Integer",
|
350
|
+
"choices": None,
|
351
|
+
"mandatory": True
|
352
|
+
},
|
353
|
+
"t_h": {
|
354
|
+
"value": self.Hours,
|
355
|
+
"explicit name": "Hours",
|
356
|
+
"description": "Number of hours for the simulation",
|
357
|
+
"type": "Integer",
|
358
|
+
"choices": None,
|
359
|
+
"mandatory": True
|
360
|
+
},
|
361
|
+
"t_min": {
|
362
|
+
"value": self.Minutes,
|
363
|
+
"explicit name": "Minutes",
|
364
|
+
"description": "Number of minutes for the simulation",
|
365
|
+
"type": "Integer",
|
366
|
+
"choices": None,
|
367
|
+
"mandatory": True
|
368
|
+
},
|
369
|
+
"t_s": {
|
370
|
+
"value": self.Seconds,
|
371
|
+
"explicit name": "Seconds",
|
372
|
+
"description": "Number of seconds for the simulation",
|
373
|
+
"type": "Integer",
|
374
|
+
"choices": None,
|
375
|
+
"mandatory": True
|
376
|
+
}
|
377
|
+
},
|
378
|
+
"Victim (-1 for unknown)": {
|
379
|
+
"n_b": {
|
380
|
+
"value": self.n_b,
|
381
|
+
"explicit name": "Number of bodies",
|
382
|
+
"description": "Number of simulated bodies",
|
383
|
+
"type": "Integer",
|
384
|
+
"choices": None,
|
385
|
+
"mandatory": True
|
386
|
+
},
|
387
|
+
"gender": {
|
388
|
+
"value": self.gender,
|
389
|
+
"explicit name": "Gender",
|
390
|
+
"description": "Gender of the victim (1 for male, 2 for female)",
|
391
|
+
"type": "Integer",
|
392
|
+
"choices": {
|
393
|
+
"Unknown":-1,
|
394
|
+
"Man":1,
|
395
|
+
"Woman":2
|
396
|
+
},
|
397
|
+
"mandatory": True
|
398
|
+
},
|
399
|
+
"age": {
|
400
|
+
"value": self.Age,
|
401
|
+
"explicit name": "Age [years]",
|
402
|
+
"description": "Age of the victim in years",
|
403
|
+
"type": "Integer",
|
404
|
+
"choices": None,
|
405
|
+
"mandatory": True
|
406
|
+
},
|
407
|
+
"h_b": {
|
408
|
+
"value": self.height,
|
409
|
+
"explicit name": "Height [m]",
|
410
|
+
"description": "Height of the victim in meters",
|
411
|
+
"type": "Float",
|
412
|
+
"choices": None,
|
413
|
+
"mandatory": True
|
414
|
+
},
|
415
|
+
"m_b": {
|
416
|
+
"value": self.mass,
|
417
|
+
"explicit name": "Mass [kg]",
|
418
|
+
"description": "Mass of the victim in kilograms",
|
419
|
+
"type": "Float",
|
420
|
+
"choices": None,
|
421
|
+
"mandatory": True
|
422
|
+
},
|
423
|
+
"BMI": {
|
424
|
+
"value": self.BMI,
|
425
|
+
"explicit name": "BMI [kg/m²]",
|
426
|
+
"description": "Body Mass Index of the victim, needed only if the mass is unknown",
|
427
|
+
"type": "Float",
|
428
|
+
"choices": None,
|
429
|
+
"mandatory": True
|
430
|
+
},
|
431
|
+
"clothes": {
|
432
|
+
"value": self.clothes,
|
433
|
+
"explicit name": "Clothing type",
|
434
|
+
"description": "Clothing type of the victim",
|
435
|
+
"type": "Integer",
|
436
|
+
"choices": {
|
437
|
+
"Unknown":-1,
|
438
|
+
"Naked":0,
|
439
|
+
"Short and T-shirt (Summer clothes)":1,
|
440
|
+
"Sweater and trousers (Spring/Fall)":2,
|
441
|
+
"Sweater, trousers and heavy warm jacket (Winter clothes)":3
|
442
|
+
},
|
443
|
+
"mandatory": True
|
444
|
+
},
|
445
|
+
"T_w": {
|
446
|
+
"value": self.T_w,
|
447
|
+
"explicit name": "Water temperature [°C]",
|
448
|
+
"description": "Average water temperature in °C",
|
449
|
+
"type": "Float",
|
450
|
+
"choices": None,
|
451
|
+
"mandatory": True
|
452
|
+
},
|
453
|
+
"ini_drowning": {
|
454
|
+
"value": self.ini_drowning,
|
455
|
+
"explicit name": "Initial drowning time",
|
456
|
+
"description": "Time at which the victim drowned",
|
457
|
+
"type": "Integer",
|
458
|
+
"choices": None,
|
459
|
+
"mandatory": True
|
460
|
+
},
|
461
|
+
"m_b_add": {
|
462
|
+
"value": self.m_b_add,
|
463
|
+
"explicit name": "Added mass of accessories [kg]",
|
464
|
+
"description": "Mass of added accessories, like a backpack or lifting weights",
|
465
|
+
"type": "Float",
|
466
|
+
"choices": None,
|
467
|
+
"mandatory": True
|
468
|
+
}
|
469
|
+
},
|
470
|
+
"Initial_drowning_point": {
|
471
|
+
"x": {
|
472
|
+
"value": self.ind_pos_0_x,
|
473
|
+
"explicit name": "X-cell",
|
474
|
+
"description": "X-coordinate of the initial drowning point",
|
475
|
+
"type": "Integer",
|
476
|
+
"choices": None,
|
477
|
+
"mandatory": True
|
478
|
+
},
|
479
|
+
"y": {
|
480
|
+
"value": self.ind_pos_0_y,
|
481
|
+
"explicit name": "Y-cell",
|
482
|
+
"description": "Y-coordinate of the initial drowning point",
|
483
|
+
"type": "Integer",
|
484
|
+
"choices": None,
|
485
|
+
"mandatory": True
|
486
|
+
}
|
487
|
+
},
|
488
|
+
"Grid": {
|
489
|
+
"origx": {
|
490
|
+
"value": self.origx,
|
491
|
+
"explicit name": "Origin X",
|
492
|
+
"description": "Origin of the matrix in X",
|
493
|
+
"type": "Float",
|
494
|
+
"choices": None,
|
495
|
+
"mandatory": True
|
496
|
+
},
|
497
|
+
"origy": {
|
498
|
+
"value": self.origy,
|
499
|
+
"explicit name": "Origin Y",
|
500
|
+
"description": "Origin of the matrix in Y",
|
501
|
+
"type": "Float",
|
502
|
+
"choices": None,
|
503
|
+
"mandatory": True
|
504
|
+
},
|
505
|
+
"dx": {
|
506
|
+
"value": self.dx,
|
507
|
+
"explicit name": "Delta X",
|
508
|
+
"description": "Spatial step of the matrix in X",
|
509
|
+
"type": "Float",
|
510
|
+
"choices": None,
|
511
|
+
"mandatory": True
|
512
|
+
},
|
513
|
+
"dy": {
|
514
|
+
"value": self.dy,
|
515
|
+
"explicit name": "Delta Y",
|
516
|
+
"description": "Spatial step of the matrix in Y",
|
517
|
+
"type": "Float",
|
518
|
+
"choices": None,
|
519
|
+
"mandatory": True
|
520
|
+
},
|
521
|
+
"nbx": {
|
522
|
+
"value": self.nbx,
|
523
|
+
"explicit name": "Nb X",
|
524
|
+
"description": "Number of steps of the matrix in X",
|
525
|
+
"type": "Integer",
|
526
|
+
"choices": None,
|
527
|
+
"mandatory": True
|
528
|
+
},
|
529
|
+
"nby": {
|
530
|
+
"value": self.nby,
|
531
|
+
"explicit name": "Nb Y",
|
532
|
+
"description": "Number of steps of the matrix in Y",
|
533
|
+
"type": "Integer",
|
534
|
+
"choices": None,
|
535
|
+
"mandatory": True
|
536
|
+
}
|
537
|
+
}
|
538
|
+
}
|
539
|
+
|
540
|
+
self.param_dict = param_dict
|
541
|
+
|
542
|
+
return
|
543
|
+
|
544
|
+
def from_dictionnary_to_attributes(self):
|
545
|
+
"""
|
546
|
+
Update the attributes of the class based on the values in self.param_dict.
|
547
|
+
"""
|
548
|
+
# Parcourir les sections et les paramètres dans param_dict
|
549
|
+
for section, params in self.param_dict.items():
|
550
|
+
for key, param_data in params.items():
|
551
|
+
# Récupérer la valeur du paramètre
|
552
|
+
value = param_data.get("value", None)
|
553
|
+
if param_data.get("type", None)=='Integer':
|
554
|
+
value = int(value)
|
555
|
+
|
556
|
+
# Mettre à jour l'attribut correspondant dans self
|
557
|
+
if section == "Options":
|
558
|
+
if key == "Profile":
|
559
|
+
self.Profile_this = value
|
560
|
+
elif key == "Save":
|
561
|
+
self.saving = value
|
562
|
+
elif key == "Load":
|
563
|
+
self.loading = value
|
564
|
+
elif key == "Plot":
|
565
|
+
self.plot_pos = value
|
566
|
+
elif key == "n_parallel":
|
567
|
+
self.n_parallel = value
|
568
|
+
elif key == "vertical":
|
569
|
+
self.Z_param.vertical = value
|
570
|
+
elif key == "a_RK":
|
571
|
+
self.a_RK = value
|
572
|
+
elif key == "image":
|
573
|
+
self.image = value
|
574
|
+
|
575
|
+
elif section == "Paths":
|
576
|
+
if key == "File":
|
577
|
+
self.file_name = value
|
578
|
+
elif key == "Save":
|
579
|
+
self.Path_saving = value
|
580
|
+
elif key == "Load":
|
581
|
+
self.Path_loading = value
|
582
|
+
elif key == "Wolf":
|
583
|
+
self.Path_Wolf = value
|
584
|
+
|
585
|
+
elif section == "DT":
|
586
|
+
if key == "CFL":
|
587
|
+
self.CFL = value
|
588
|
+
elif key == "dt_min":
|
589
|
+
self.dt_min = value
|
590
|
+
elif key == "dt_max":
|
591
|
+
self.dt_max = value
|
592
|
+
|
593
|
+
elif section == "Duration":
|
594
|
+
if key == "Days":
|
595
|
+
self.Days = value
|
596
|
+
elif key == "Hours":
|
597
|
+
self.Hours = value
|
598
|
+
elif key == "Minutes":
|
599
|
+
self.Minutes = value
|
600
|
+
elif key == "Seconds":
|
601
|
+
self.Seconds = value
|
602
|
+
|
603
|
+
elif section == "Victim (-1 for unknown)":
|
604
|
+
if key == "n_b":
|
605
|
+
self.n_b = value
|
606
|
+
elif key == "gender":
|
607
|
+
self.gender = value
|
608
|
+
elif key == "age":
|
609
|
+
self.Age = value
|
610
|
+
elif key == "h_b":
|
611
|
+
self.height = value
|
612
|
+
elif key == "m_b":
|
613
|
+
self.mass = value
|
614
|
+
elif key == "BMI":
|
615
|
+
self.BMI = value
|
616
|
+
elif key == "clothes":
|
617
|
+
self.clothes = value
|
618
|
+
elif key == "T_w":
|
619
|
+
self.T_w = value
|
620
|
+
elif key == "ini_drowning":
|
621
|
+
self.ini_drowning = value
|
622
|
+
elif key == "m_b_add":
|
623
|
+
self.m_b_add = value
|
624
|
+
|
625
|
+
elif section == "Initial_drowning_point":
|
626
|
+
if key == "x":
|
627
|
+
self.ind_pos_0_x = value
|
628
|
+
elif key == "y":
|
629
|
+
self.ind_pos_0_y = value
|
630
|
+
|
631
|
+
elif section == "Grid":
|
632
|
+
if key == "Origin X":
|
633
|
+
self.origx = value
|
634
|
+
elif key == "Origin Y":
|
635
|
+
self.origy = value
|
636
|
+
elif key == "Delta X":
|
637
|
+
self.dx = value
|
638
|
+
elif key == "Delta Y":
|
639
|
+
self.dy = value
|
640
|
+
elif key == "Nb X":
|
641
|
+
self.nbx = value
|
642
|
+
elif key == "Nb Y":
|
643
|
+
self.nby = value
|
644
|
+
|
645
|
+
# Initialise the parameters of the simulation with default values and values given in the parameters.param file
|
646
|
+
self.t_initial = 0
|
647
|
+
self.i_initial = 0
|
648
|
+
self.time_goal = self.Days*24*60*60 + self.Hours*60*60 + self.Minutes*60 + self.Seconds #s
|
649
|
+
self.wanted_time = np.array([self.t_initial])
|
650
|
+
self.n_saved = 1
|
651
|
+
for i in np.arange(10,self.time_goal+10,10):
|
652
|
+
if i<60:
|
653
|
+
self.wanted_time = np.append(self.wanted_time,i)
|
654
|
+
self.n_saved += 1
|
655
|
+
elif np.logical_and(i<10*60,(i%60==0)):
|
656
|
+
self.wanted_time = np.append(self.wanted_time,i)
|
657
|
+
self.n_saved += 1
|
658
|
+
elif np.logical_and(i<30*60,(i%(5*60)==0)):
|
659
|
+
self.wanted_time = np.append(self.wanted_time,i)
|
660
|
+
self.n_saved += 1
|
661
|
+
elif np.logical_and(i<60*60,(i%(10*60)==0)):
|
662
|
+
self.wanted_time = np.append(self.wanted_time,i)
|
663
|
+
self.n_saved += 1
|
664
|
+
elif np.logical_and(i<=24*60*60,(i%(60*60)==0)):
|
665
|
+
self.wanted_time = np.append(self.wanted_time,i)
|
666
|
+
self.n_saved += 1
|
667
|
+
elif np.logical_and(i<=2*24*60*60,(i%(2*60*60)==0)):
|
668
|
+
self.wanted_time = np.append(self.wanted_time,i)
|
669
|
+
self.n_saved += 1
|
670
|
+
elif (i%(3*60*60)==0):
|
671
|
+
self.wanted_time = np.append(self.wanted_time,i)
|
672
|
+
self.n_saved += 1
|
673
|
+
|
674
|
+
self.wanted_time = np.append(self.wanted_time,0)
|
675
|
+
|
676
|
+
self.n_t = math.floor(self.time_goal/self.dt_min)+1
|
677
|
+
self.count_initial = 1
|
678
|
+
self.count_pre = self.count_initial-1
|
679
|
+
|
680
|
+
n_b = self.n_b
|
681
|
+
|
682
|
+
self.random_IP = 1 # number of cells considered for the radius of random position
|
683
|
+
|
684
|
+
## Parameters of vertical motion (ADD, temp)
|
685
|
+
|
686
|
+
self.DZ = 0.1
|
687
|
+
|
688
|
+
# Update of the dataframe
|
689
|
+
Z_param = pd.DataFrame(data=None,columns=COLUMN_Z_PARAM,dtype=np.int32)
|
690
|
+
Z_param.U_z = 0*np.ones((n_b)) #0 = U constant on depth, 1 = U varies with the depth (log law)
|
691
|
+
d_50 = 2*2*40 *10**-3 #to be confirmed
|
692
|
+
Z_param.z_0 = d_50/30*np.ones((n_b)) #experimental results of Nikuradse (not found if published in 1933 or 1950 but nobody seems to care)
|
693
|
+
Z_param.mu_stat = 1 * np.ones((n_b)) #rnd.beta(1,1,size=(n_b))*(1-0.3)+0.3
|
694
|
+
Z_param.Time_float = 0*np.ones((n_b))
|
695
|
+
Z_param.T_w = self.T_w*np.ones((n_b))
|
696
|
+
Z_param.ADD = self.time_goal/60/60/24*self.T_w
|
697
|
+
Z_param.ADD_resurface = 5250/self.T_w * rnd.beta(4,4,size=n_b) #source: Heaton 2011 considering a TADS between 14 and 15 as maximum expension
|
698
|
+
self.Z_param = Z_param
|
699
|
+
|
700
|
+
def from_dot_param_to_dictionnary(self,store_dir: Path = None):
|
701
|
+
"""
|
702
|
+
Update the parameters with the modifications made by the user with the file parameters.param
|
703
|
+
|
704
|
+
:param store_dir: directory where the file parameters.param is
|
705
|
+
"""
|
706
|
+
|
707
|
+
# Charger le dictionnaire existant
|
708
|
+
param_dict = self.param_dict
|
709
|
+
|
710
|
+
data = {}
|
711
|
+
text_file_path = join(store_dir,"parameters.param")
|
712
|
+
|
713
|
+
if not os.path.exists(text_file_path):
|
714
|
+
raise FileNotFoundError(f"Le fichier {text_file_path} est introuvable.")
|
715
|
+
|
716
|
+
with open(text_file_path, 'r', encoding='ISO-8859-1') as file:
|
717
|
+
for line in file:
|
718
|
+
line = line.strip()
|
719
|
+
|
720
|
+
# Vérifier si la ligne est un nom de section (par exemple, 'Options:', 'Path:', etc.)
|
721
|
+
if line.endswith(":"):
|
722
|
+
# Crée une nouvelle sous-section
|
723
|
+
current_section = line[:-1] # Retire le ':' pour obtenir le nom de section
|
724
|
+
data[current_section] = {}
|
725
|
+
elif "\t" in line and current_section:
|
726
|
+
# Split clé et valeur
|
727
|
+
key, value = line.split("\t", 1)
|
728
|
+
|
729
|
+
# Convertir la valeur en nombre si possible
|
730
|
+
try:
|
731
|
+
value = float(value)
|
732
|
+
except ValueError:
|
733
|
+
pass # Garde la valeur comme chaîne de caractères si non convertible
|
734
|
+
|
735
|
+
# Ajout de la clé et de la valeur dans la section actuelle
|
736
|
+
data[current_section][key] = value
|
737
|
+
|
738
|
+
# Mettre à jour self.param_dict avec les valeurs de data
|
739
|
+
for section, params in data.items():
|
740
|
+
if section in param_dict:
|
741
|
+
for key, value in params.items():
|
742
|
+
# Rechercher la clé dans le dictionnaire existant
|
743
|
+
for param_key, param_data in param_dict[section].items():
|
744
|
+
if param_data["explicit name"] == key:
|
745
|
+
# Mettre à jour la valeur
|
746
|
+
param_dict[section][param_key]["value"] = value
|
747
|
+
break
|
748
|
+
|
749
|
+
# Sauvegarder le dictionnaire mis à jour dans self.param_dict
|
750
|
+
self.param_dict = param_dict
|
751
|
+
|
752
|
+
return
|
753
|
+
|
754
|
+
def Human_generation(self):
|
755
|
+
"""
|
756
|
+
Generates the bodies for the simulation
|
757
|
+
|
758
|
+
:return Human
|
759
|
+
|
760
|
+
Attributes:
|
761
|
+
Human : Dataframe panda with each line representing a body and n_b lines, so one for eahc body
|
762
|
+
gender : Gender of the victim, 1 for man, 2 for women
|
763
|
+
Age : Age of the victim in years
|
764
|
+
height : Height of the victim in m
|
765
|
+
mass : Mass of the victim in kg
|
766
|
+
BMI : BMI of the victim in kg/m²
|
767
|
+
clothes : clothing type of the victim (0 for naked, 1 for summer clothes, 2 for spring clothes, 3 for winter clothes)
|
768
|
+
CAM : Added mass coefficient of the body
|
769
|
+
CDA : Drag area of the body (drag coefficient * a reference area)
|
770
|
+
CLA : Lift area of the body
|
771
|
+
CSA : Side area of the body
|
772
|
+
fp_x : Projection coefficient along the x-axis to go from the BSA to the frontal area
|
773
|
+
fp_y : Projection coefficient along the y-axis to go from the BSA to the frontal area
|
774
|
+
fp_z : Projection coefficient along the z-axis to go from the BSA to the frontal area
|
775
|
+
lungs_volume_TLC : Lungs volume at Total Lungs Capacity
|
776
|
+
lungs_volume_FRC : Lungs volume at Functionnal Residual Capacity (at rest, after normally expiring)
|
777
|
+
dm : Amount of swallowed water
|
778
|
+
BSA : Body surface area (i.e. surface of the skin)
|
779
|
+
Death : Type of death
|
780
|
+
eps : Width of the body
|
781
|
+
V_clothes_o : Initial volume of clothes (according to Barwood et al., 2011)
|
782
|
+
V_clothes_one : Volume of clothes after 20min at rest (according to Barwood et al., 2011)
|
783
|
+
V_clothes_two : Volume of clothes after 20min of swimming (according to Barwood et al., 2011)
|
784
|
+
error_perc_fat : Deviation to the average on the percentage body fat of the body calculated from the equation of Siri et al. 1960
|
785
|
+
|
786
|
+
"""
|
787
|
+
|
788
|
+
Human = pd.DataFrame(data=None,columns=COLUMNS_HUMAN,dtype=np.int32)
|
789
|
+
|
790
|
+
n_b = self.n_b
|
791
|
+
|
792
|
+
##Gender
|
793
|
+
Human.gender = self.gender * np.ones((n_b))
|
794
|
+
if self.gender == -1:
|
795
|
+
Human.gender = np.zeros(n_b)
|
796
|
+
Human.gender[:n_b // 2] = 2
|
797
|
+
Human.gender[n_b // 2:] = 1
|
798
|
+
ind_m = np.where(Human.gender==1)[0]
|
799
|
+
ind_w = np.where(Human.gender==2)[0]
|
800
|
+
|
801
|
+
##Age
|
802
|
+
Human.Age = self.Age * np.ones((n_b))
|
803
|
+
if self.Age==-1:
|
804
|
+
age_min = 18
|
805
|
+
age_max = 90 + 1
|
806
|
+
Human.Age = rnd.randint(age_min,age_max,size=(n_b))
|
807
|
+
|
808
|
+
#Height
|
809
|
+
h_av = self.height
|
810
|
+
h_max = np.array([205, 190]) /100
|
811
|
+
h_min = np.array([150, 140]) /100
|
812
|
+
[ah_w,bh_w] = known_1(2,h_min[1],h_max[1],h_av-0.025,h_av+0.025,0.1,0.9)
|
813
|
+
[ah_m,bh_m] = known_1(1,h_min[0],h_max[0],h_av-0.025,h_av+0.025,0.1,0.9)
|
814
|
+
Human.loc[ind_m,'height'] = rnd.beta(ah_m,bh_m,size=(len(ind_m)))*(h_max[0]-h_min[0])+h_min[0] #men
|
815
|
+
Human.loc[ind_w,'height'] = rnd.beta(ah_w,bh_w,size=(len(ind_w)))*(h_max[1]-h_min[1])+h_min[1] #women
|
816
|
+
if h_av == -1:
|
817
|
+
Human.loc[ind_m,'height'] = rnd.beta(5.8697,6.075,size=(len(ind_m)))*(h_max[0]-h_min[0])+h_min[0] #men
|
818
|
+
Human.loc[ind_w,'height'] = rnd.beta(3.976,5.965,size=(len(ind_w)))*(h_max[1]-h_min[1])+h_min[1] #women
|
819
|
+
|
820
|
+
##Mass or BMI
|
821
|
+
m_av = self.mass
|
822
|
+
m_max = np.array([135, 130])
|
823
|
+
m_min = np.array([35, 35])
|
824
|
+
[am_w,bm_w] = known_1(2+3,m_min[1],m_max[1],m_av-2.5,m_av+2.5,0.1,0.9)
|
825
|
+
[am_m,bm_m] = known_1(1+3,m_min[0],m_max[0],m_av-2.5,m_av+2.5,0.1,0.9)
|
826
|
+
Human.loc[ind_m,'mass'] = rnd.beta(am_m,bm_m,size=((len(ind_m))))*(m_max[0] - m_min[0]) + m_min[0]
|
827
|
+
Human.loc[ind_w,'mass'] = rnd.beta(am_w,bm_w,size=((len(ind_w))))*(m_max[1] - m_min[1]) + m_min[1]
|
828
|
+
Human.BMI = Human.mass / Human.height**2
|
829
|
+
known = 1
|
830
|
+
if m_av < 0:
|
831
|
+
BMI = self.BMI
|
832
|
+
BMI_min = 16
|
833
|
+
BMI_max = 40
|
834
|
+
BMI_25 = [20, 21.3, 22.5, 23.3, 22.9, 23.7, 23.1]
|
835
|
+
BMI_50 = [21.7, 23.4, 24.8, 25.7, 25.9, 26.3, 25.3]
|
836
|
+
BMI_75 = [24.3, 26.4, 28, 29, 29.1, 29.7, 28]
|
837
|
+
ind_Age = np.minimum(math.floor(np.mean(Human.Age.to_numpy())/10)-1,6)
|
838
|
+
if BMI > 0:
|
839
|
+
BMI_down = BMI-1
|
840
|
+
BMI_up = BMI+1
|
841
|
+
[abmi,bbmi] = known_1(3,BMI_min,BMI_max,BMI_down,BMI_up,0.1,0.9)
|
842
|
+
Human.BMI = rnd.beta(abmi,bbmi,size=((n_b)))*(BMI_max-BMI_min)+BMI_min
|
843
|
+
else:
|
844
|
+
[abmi,bbmi] = known_1(3,BMI_min,BMI_max,BMI_25[ind_Age],BMI_75[ind_Age],0.25,0.75)
|
845
|
+
Human.BMI = rnd.beta(abmi,bbmi,size=((n_b)))*(BMI_max-BMI_min)+BMI_min
|
846
|
+
known = 0
|
847
|
+
Human.mass = Human.BMI*Human.height**2
|
848
|
+
|
849
|
+
##Clothes
|
850
|
+
clothes = self.clothes #simpledialog.askinteger('Dialog title','Clothing type with: \n-1 if unknown \n0 for naked or in underwear\n1 for summer clothes (short and a t-shirt or dress)\n2 for autumn/spring clothes (trousers, a t-shirt, a sweater, and eventually a water/windproof jacket)\n3 for winter clothes (trousers, a t-shirt, a sweater, and a heavy warm jacket or more clothes)',minvalue=-1,maxvalue=3,parent=root) #0 for naked, 1 for summer clothes, 2 for autumn/spring clothes, 3 for winter clothes
|
851
|
+
if clothes==-1:
|
852
|
+
clothes=2
|
853
|
+
|
854
|
+
Human,error_perc_fat = Skinfold(n_b,known,Human)
|
855
|
+
|
856
|
+
|
857
|
+
Human.CAM = ((2-Human.gender)*0.268+(Human.gender-1)*0.236) *np.ones((n_b)) #according Caspersen et al., 2010: Added mass in human swimmers
|
858
|
+
|
859
|
+
#CD, CL and CS fitted to the results of tests realised in the wind tunnel on 28/02/2023, tight clothing = no clothes or summer clothes
|
860
|
+
Human.CDA = 0.4181*np.ones(n_b)*(clothes<2) + 0.5172*np.ones(n_b)*(clothes>=2)
|
861
|
+
Human.CLA = 0.07019*np.ones(n_b)*(clothes<2) + 0.08387*np.ones(n_b)*(clothes>=2)
|
862
|
+
Human.CSA = 0.03554*np.ones(n_b)*(clothes<2) + 0.04047*np.ones(n_b)*(clothes>=2)
|
863
|
+
# Human.CDA = rnd.normal(0.4181,0.03434,n_b)*(clothes<2) + rnd.normal(0.5172,0.0406,n_b)*(clothes>=2)
|
864
|
+
# Human.CLA = rnd.normal(0.07019,0.05035,n_b)*(clothes<2) + rnd.normal(0.08387,0.08138,n_b)*(clothes>=2)
|
865
|
+
# Human.CSA = rnd.normal(0.03554,0.02545,n_b)*(clothes<2) + rnd.normal(0.04047,0.04311,n_b)*(clothes>=2)
|
866
|
+
|
867
|
+
#Mandatory for the structure of the numpy variable Human generated for the calculations
|
868
|
+
Human.fp_x = rnd.beta(1,1,size=(n_b))*(0.36-0.16)+0.16
|
869
|
+
Human.fp_y = 0.36-Human.fp_x+0.16
|
870
|
+
Human.fp_z = np.ones((n_b)) * 0.2
|
871
|
+
|
872
|
+
|
873
|
+
Human.lungs_volume_TLC = 10**-3 * ((7.99*Human.height-7.08) * (2-Human.gender) + (6.6*Human.height-5.79) * (Human.gender-1)) #Formulas ERC valid for men between 1.55 and 1.95 m and women between 1.45 and 1.8 m
|
874
|
+
Human.lungs_volume_FRC = 10**-3 * ((2.34*Human.height+0.01*Human.Age-1.09) * (2-Human.gender) + (2.24*Human.height+0.001*Human.Age-1) * (Human.gender-1)) #Formulas ERC valid for men between 1.55 and 1.95 m and women between 1.45 and 1.8 m
|
875
|
+
Human.lungs_volume_TLC = Human.lungs_volume_TLC * (0.0403*Human.BMI**2 - 3.1049*Human.BMI + 149.58)/100 #Digitalization of TOTAL part of figure 4 of doi:10.1136/bmjresp-2017-000231
|
876
|
+
Human.lungs_volume_FRC = Human.lungs_volume_FRC * (0.102*Human.BMI**2 - 7.4504*Human.BMI + 229.61)/100 #Digitalization of TOTAL part of figure 4 of doi:10.1136/bmjresp-2017-000231
|
877
|
+
|
878
|
+
Human.dm = Human.mass * (0.1 * rnd.beta(1.5,1.5,size=((n_b))) + 0.0) #Between x and y% of the body mass (usually around 10 according to test on dogs)
|
879
|
+
Human.dm += self.m_b_add
|
880
|
+
|
881
|
+
Human.BSA = ((128.1 * Human.mass**0.44 * (Human.height*100)**0.6) * (2-Human.gender) + (147.4 * Human.mass**0.47 * (Human.height*100)**0.55) * (Human.gender-1))*10**-4
|
882
|
+
|
883
|
+
Human.Death = np.ones(n_b)
|
884
|
+
|
885
|
+
Human.eps = np.ones(n_b)*0.2
|
886
|
+
|
887
|
+
|
888
|
+
clothes_alpha_m = np.array([[1,0.1771,0.0192,0.0082],[1,5.7342e-6,0.0253,0.8511],[1,5.9719e-6,0.333,4.981e-6]])
|
889
|
+
clothes_alpha_w = np.array([[1,0.7676,4.4037,0.8523],[1,0.5375,0.3333,3.5128],[1,0.5375,0.7082,2.333]])
|
890
|
+
clothes_beta_m = np.array([[1,0.3542,0.0385,0.0095],[1,9.9565e-6,0.0505,1.7022],[1,1.1522e-5,0.667,8.7348e-6]])
|
891
|
+
clothes_beta_w = np.array([[1,1.5353,8.8072,1.7046],[1,1.0749,0.6667,7.0255],[1,1.0749,1.4165,4.6659]])
|
892
|
+
|
893
|
+
clothes_mean_m = np.array([[0.5,0.6127,2.7573,4.3912],[0.5,0.204,0.715,1.021],[0.5,0.408,1.1233,0.613]])*10**-3
|
894
|
+
clothes_mean_w = np.array([[0.5,1.123,2.655,4.493],[0.5,0.9191,1.1233,1.94],[0.5,0.9191,1.2254,2.0424]])*10**-3
|
895
|
+
clothes_std_m = np.array([[0.5,0.7148,0.817,0.6127],[0.5,0.9191,1.1233,0.817],[0.5,0.817,1.1233,2.451]])*10**-3
|
896
|
+
clothes_std_w = np.array([[0.5,0.9191,1.6339,1.6339],[0.5,0.817,1.1233,1.225],[0.5,0.817,1.0212,0.817]])*10**-3
|
897
|
+
|
898
|
+
clothes_max_m = clothes_mean_m + 2*clothes_std_m
|
899
|
+
clothes_min_m = clothes_mean_m - 2*clothes_std_m
|
900
|
+
clothes_max_w = clothes_mean_w + 2*clothes_std_w
|
901
|
+
clothes_min_w = clothes_mean_w - 2*clothes_std_w
|
902
|
+
|
903
|
+
|
904
|
+
Human.V_clothes_o = (clothes!=0)* ((rnd.beta(clothes_alpha_m[0,clothes],clothes_beta_m[0,clothes],size=(n_b))*(clothes_max_m[0,clothes]-clothes_min_m[0,clothes])+clothes_min_m[0,clothes])*(2-Human.gender) + (rnd.beta(clothes_alpha_w[0,clothes],clothes_beta_w[0,clothes],size=(n_b))*(clothes_max_w[0,clothes]-clothes_min_w[0,clothes])+clothes_min_w[0,clothes])*(Human.gender-1))
|
905
|
+
Human.V_clothes_one = (clothes!=0)* ((rnd.beta(clothes_alpha_m[1,clothes],clothes_beta_m[1,clothes],size=(n_b))*(clothes_max_m[1,clothes]-clothes_min_m[1,clothes])+clothes_min_m[1,clothes])*(2-Human.gender) + (rnd.beta(clothes_alpha_w[1,clothes],clothes_beta_w[1,clothes],size=(n_b))*(clothes_max_w[1,clothes]-clothes_min_w[1,clothes])+clothes_min_w[1,clothes])*(Human.gender-1))
|
906
|
+
Human.V_clothes_two = (clothes!=0)* ((rnd.beta(clothes_alpha_m[2,clothes],clothes_beta_m[2,clothes],size=(n_b))*(clothes_max_m[2,clothes]-clothes_min_m[2,clothes])+clothes_min_m[2,clothes])*(2-Human.gender) + (rnd.beta(clothes_alpha_w[2,clothes],clothes_beta_w[2,clothes],size=(n_b))*(clothes_max_w[2,clothes]-clothes_min_w[2,clothes])+clothes_min_w[2,clothes])*(Human.gender-1))
|
907
|
+
|
908
|
+
Human.error_perc_fat = error_perc_fat
|
909
|
+
|
910
|
+
self.Human = Human
|
911
|
+
|
912
|
+
def Initialisation_arrays(self):
|
913
|
+
"""
|
914
|
+
Function where the matrixes of body position, speed, time, resurfacing and sinking are initialised, both for computing and saving
|
915
|
+
Initialisation of other variables used in the simulation
|
916
|
+
|
917
|
+
Attributes:
|
918
|
+
|
919
|
+
BC_cells : Array containing the index of all cells that are boundary conditions for the hydrodynamic simulation
|
920
|
+
DT_WOLF : Time step of the WOLF simulation
|
921
|
+
NbX : Number of cells in the x-direction for the WOLF simulation
|
922
|
+
NbY : Number of cells in the y-direction for the WOLF simulation
|
923
|
+
ini_drowning : Hour at which the victim fell in the water
|
924
|
+
count_Wolf : Time step of the Wolf simulation that we consider as our initial time in the Lagrangian simulation
|
925
|
+
wanted_Wolf : Array containing all the times at which we have a new Wolf result
|
926
|
+
Delta : Array containing the spatial and time steps
|
927
|
+
Pos : Working array containing the 3D positions of all bodies at time t and t-dt with shape (n_b,3,2)
|
928
|
+
Pos_b : Saving array containing the 3D positions of all bodies at all saving times with shape (n_b,3,n_t)
|
929
|
+
U : Working array containing the 3D velocities of all bodies at time t and t-dt with shape (n_b,3,2)
|
930
|
+
U_b : Saving array containing the 3D velocities of all bodies at all saving times with shape (n_b,3,n_t)
|
931
|
+
time : Working array containing the time associated to each body with shape (n_b,)
|
932
|
+
resurface : Saving array containing the resurfacing time of all bodies with shape (n_b,)
|
933
|
+
sinking : Saving array containing the sinking time of all bodies with shape (n_b,)
|
934
|
+
count : Counter to evaluate the progression of the savings
|
935
|
+
sp : Parameter deserving to work with the working variables
|
936
|
+
"""
|
937
|
+
|
938
|
+
self.Human_generation()
|
939
|
+
n_b = self.n_b
|
940
|
+
n_saved = self.n_saved
|
941
|
+
|
942
|
+
self.BC_cells,self.DT_WOLF,DX,DY,H_mat,self.NbX,self.NbY,t_tot_Wolf = Read_Wolf_GPU_metadata(self.Path_Wolf)
|
943
|
+
|
944
|
+
X = np.arange(0,DX*self.NbX,DX)+DX/2
|
945
|
+
Y = np.arange(0,DY*self.NbY,DY)+DY/2
|
946
|
+
|
947
|
+
self.count_Wolf = self.ini_drowning -1
|
948
|
+
self.wanted_Wolf = np.arange(0,t_tot_Wolf+self.DT_WOLF,self.DT_WOLF)
|
949
|
+
|
950
|
+
|
951
|
+
Delta = np.zeros((5))
|
952
|
+
Delta[0] = DX
|
953
|
+
Delta[1] = DY
|
954
|
+
Delta[2] = 1 #DZ
|
955
|
+
Delta[3] = self.dt_max
|
956
|
+
Delta[4] = np.sqrt(DX**2+DY**2)
|
957
|
+
self.Delta = Delta
|
958
|
+
|
959
|
+
ind_pos_0_x = self.ind_pos_0_x
|
960
|
+
ind_pos_0_y = self.ind_pos_0_y
|
961
|
+
NbZ = H_mat[ind_pos_0_y,ind_pos_0_x]/Delta[2]
|
962
|
+
ind_pos_0_z = NbZ.astype(int)
|
963
|
+
|
964
|
+
index_b = np.zeros((n_b,3,n_saved)) #number of the body,(xyz) index in the matrix, time step
|
965
|
+
index_b = index_b.astype(int)
|
966
|
+
|
967
|
+
Pos_b = np.zeros((n_b,3,n_saved)) #number of the body,(xyz), time step
|
968
|
+
|
969
|
+
U_b = np.zeros((n_b,3,n_saved)) #number of the body,(xyz), time step
|
970
|
+
|
971
|
+
self.time_b = np.zeros((n_b,n_saved))
|
972
|
+
|
973
|
+
index_b[:,0,0] = np.ones((n_b)) * ind_pos_0_x
|
974
|
+
index_b[:,1,0] = np.ones((n_b)) * ind_pos_0_y
|
975
|
+
index_b[:,2,0] = np.zeros((n_b))
|
976
|
+
|
977
|
+
Pos_b[:,0,0] = np.ones((n_b)) * X[ind_pos_0_x]
|
978
|
+
Pos_b[:,1,0] = np.ones((n_b)) * Y[ind_pos_0_y]
|
979
|
+
|
980
|
+
## Calculation of horizontal and vertical body motion
|
981
|
+
|
982
|
+
Pos = np.zeros((n_b,3,2))
|
983
|
+
U = np.zeros((n_b,3,2))
|
984
|
+
|
985
|
+
# Generation of uncertainty on the drownin point
|
986
|
+
if self.loading == 0:
|
987
|
+
rand_x = DX* rnd.uniform(size=(n_b))*np.sign(rnd.uniform(size=(n_b))-1/2)*self.random_IP
|
988
|
+
rand_y = DY* rnd.uniform(size=(n_b))*np.sign(rnd.uniform(size=(n_b))-1/2)*self.random_IP
|
989
|
+
|
990
|
+
Pos[:,0,0] = X[int(index_b[0,0,0])]+rand_x
|
991
|
+
Pos[:,1,0] = Y[int(index_b[0,1,0])]+rand_y
|
992
|
+
Pos[:,2,0] = H_mat[int(index_b[0,1,0]),int(index_b[0,0,0])]
|
993
|
+
Pos[:,0,1] = X[int(index_b[0,0,0])]+rand_x
|
994
|
+
Pos[:,1,1] = Y[int(index_b[0,1,0])]+rand_y
|
995
|
+
Pos[:,2,1] = H_mat[int(index_b[0,1,0]),int(index_b[0,0,0])]
|
996
|
+
Pos_b[:,:,0] = Pos[:,:,0]
|
997
|
+
|
998
|
+
else:
|
999
|
+
self.count_initial,self.Human,n_loaded,Pos_b,self.time_b,U_b,self.Z_param = Loading(self.Path_loading,Pos_b,self.time_b,U_b)
|
1000
|
+
|
1001
|
+
Pos[:,0,0] = Pos_b[:,0,n_loaded]
|
1002
|
+
Pos[:,1,0] = Pos_b[:,1,n_loaded]
|
1003
|
+
Pos[:,2,0] = Pos_b[:,2,n_loaded]
|
1004
|
+
Pos[:,0,1] = Pos_b[:,0,n_loaded]
|
1005
|
+
Pos[:,1,1] = Pos_b[:,1,n_loaded]
|
1006
|
+
Pos[:,2,1] = Pos_b[:,2,n_loaded]
|
1007
|
+
|
1008
|
+
U[:,0,0] = U_b[:,0,n_loaded]
|
1009
|
+
U[:,1,0] = U_b[:,1,n_loaded]
|
1010
|
+
U[:,2,0] = U_b[:,2,n_loaded]
|
1011
|
+
U[:,0,1] = U_b[:,0,n_loaded]
|
1012
|
+
U[:,1,1] = U_b[:,1,n_loaded]
|
1013
|
+
U[:,2,1] = U_b[:,2,n_loaded]
|
1014
|
+
|
1015
|
+
self.Pos = Pos
|
1016
|
+
self.Pos_b = Pos_b
|
1017
|
+
|
1018
|
+
self.U = U
|
1019
|
+
self.U_b = U_b
|
1020
|
+
|
1021
|
+
self.time = self.t_initial*np.ones((n_b))
|
1022
|
+
self.resurface = np.zeros((n_b,2))
|
1023
|
+
self.sinking = np.zeros((n_b,2))
|
1024
|
+
self.count = self.count_initial
|
1025
|
+
self.sp = 1
|
1026
|
+
|
1027
|
+
def start(self):
|
1028
|
+
|
1029
|
+
"""
|
1030
|
+
Main of the class, runs the code with a parallelised code (n_parallel>1) or without
|
1031
|
+
|
1032
|
+
"""
|
1033
|
+
|
1034
|
+
start = timeit.default_timer()
|
1035
|
+
|
1036
|
+
self.Initialisation_arrays()
|
1037
|
+
|
1038
|
+
if self.Profile_this ==1:
|
1039
|
+
profiler = cProfile.Profile()
|
1040
|
+
profiler.enable()
|
1041
|
+
|
1042
|
+
# Conversion of dataframe to numpy array for parallel processing and memory efficiency
|
1043
|
+
Human_np = self.Human.to_numpy()
|
1044
|
+
Z_param_np = self.Z_param.to_numpy()
|
1045
|
+
|
1046
|
+
# Multiprocess run
|
1047
|
+
if self.n_parallel>1:
|
1048
|
+
# Set up progress queue for inter-process communication
|
1049
|
+
with multiprocessing.Manager() as manager:
|
1050
|
+
progress_queue = manager.Queue()
|
1051
|
+
stop_event = threading.Event() # Indicateur pour arrêter le thread
|
1052
|
+
|
1053
|
+
# Start wxPython application and create the frame
|
1054
|
+
app = wx.App(False)
|
1055
|
+
frame = wx.Frame(None)
|
1056
|
+
if self.image==1:
|
1057
|
+
frame = ProgressImage(self.n_parallel,self.time_goal, None)
|
1058
|
+
else:
|
1059
|
+
frame = ProgressBar(None,n_processes=self.n_parallel,total=self.time_goal)
|
1060
|
+
frame.Show()
|
1061
|
+
|
1062
|
+
tasks = Preparation_parallelisation(progress_queue,self.a_RK,self.BC_cells,self.count,self.count_Wolf,self.CFL,self.Delta,Human_np,self.i_initial,self.n_b,self.n_saved,self.n_parallel,self.n_t,self.NbX,self.NbY,self.Path_saving,self.Path_Wolf,self.Pos,self.Pos_b,self.resurface,self.sinking,self.time,self.time_b,self.time_goal,self.U,self.U_b,self.wanted_time,self.wanted_Wolf,Z_param_np)
|
1063
|
+
|
1064
|
+
# Création de la thread de suivi de la progression
|
1065
|
+
time_viewer = 1
|
1066
|
+
monitor_thread = threading.Thread(target=state_of_run, args=(progress_queue,frame, time_viewer))
|
1067
|
+
monitor_thread.daemon = True # Ensure it terminates when the program exits
|
1068
|
+
monitor_thread.start()
|
1069
|
+
|
1070
|
+
with multiprocessing.Pool(processes=self.n_parallel) as pool:
|
1071
|
+
result_async = pool.map_async(Parallel_loop, tasks)
|
1072
|
+
while not result_async.ready():
|
1073
|
+
wx.Yield() # This allows the GUI to update while waiting for the results
|
1074
|
+
# Wait for the result to be ready
|
1075
|
+
results = result_async.get()
|
1076
|
+
|
1077
|
+
|
1078
|
+
def on_close(event):
|
1079
|
+
frame.Close() # Ferme la fenêtre
|
1080
|
+
app.ExitMainLoop()
|
1081
|
+
|
1082
|
+
frame.Bind(wx.EVT_CLOSE, on_close)
|
1083
|
+
frame.Close()
|
1084
|
+
stop_event.set() # Stop the monitoring thread
|
1085
|
+
|
1086
|
+
self.Pos_b = np.concatenate([result[0] for result in results], axis=0)
|
1087
|
+
self.resurface = np.concatenate([result[1] for result in results], axis=0)
|
1088
|
+
self.sinking = np.concatenate([result[2] for result in results], axis=0)
|
1089
|
+
self.time_b = np.concatenate([result[3] for result in results], axis=0)
|
1090
|
+
self.U_b = np.concatenate([result[4] for result in results], axis=0)
|
1091
|
+
|
1092
|
+
# No use of multiprocessing
|
1093
|
+
else:
|
1094
|
+
self.Pos_b,self.resurface,self.sinking,self.time_b,self.U_b = Loop_management(-1,-1,self.a_RK,self.BC_cells,self.count,self.count_Wolf,self.CFL,self.Delta,Human_np,self.i_initial,self.n_b,self.n_saved,self.n_t,self.NbX,self.NbY,self.Path_saving,self.Path_Wolf,self.Pos,self.Pos_b,self.resurface,self.sinking,self.time,self.time_b,self.time_goal,self.U,self.U_b,self.wanted_time,self.wanted_Wolf,Z_param_np)
|
1095
|
+
|
1096
|
+
|
1097
|
+
stop = timeit.default_timer()
|
1098
|
+
execution_time = stop - start
|
1099
|
+
|
1100
|
+
n_b = self.n_b
|
1101
|
+
time_goal = self.time_goal
|
1102
|
+
|
1103
|
+
# Save of the results
|
1104
|
+
Path_save = os.path.join(self.Path_saving,'Results')
|
1105
|
+
os.makedirs(Path_save,exist_ok=True)
|
1106
|
+
np.savez(Path_save,Pos_b=self.Pos_b,U_b=self.U_b,Human=self.Human,Z_param=self.Z_param,wanted_time=self.wanted_time,time_b=self.time_b)
|
1107
|
+
|
1108
|
+
logging.info(f"Program executed in "+str(round(execution_time/60,1))+" min, for "+str(n_b)+" bodies and "+str(np.floor(time_goal/(60*60*24)))+" days "+str(int((time_goal/60/60-24*int(time_goal/60/60/24))))+" h "+str((np.floor((time_goal/60)%60)))+" min "+str(time_goal%60)+ " s")
|
1109
|
+
if self.Profile_this ==1:
|
1110
|
+
profiler.disable()
|
1111
|
+
stats = pstats.Stats(profiler).sort_stats('ncalls')
|
1112
|
+
stats.sort_stats('tottime')
|
1113
|
+
stats.print_stats()
|
1114
|
+
|
1115
|
+
|
1116
|
+
if self.saving == 1:
|
1117
|
+
np.savez(self.Path_saving,Pos_b=self.Pos_b,U_b=self.U_b,Human=self.Human,Z_param=self.Z_param,wanted_time=self.wanted_time,time_b=self.time_b)
|
1118
|
+
|
1119
|
+
def Parallel_loop(args):
|
1120
|
+
"""
|
1121
|
+
Necessary for the parallelisation as we have to give a list of arguments to the function instead of all the args separately
|
1122
|
+
"""
|
1123
|
+
|
1124
|
+
result = Loop_management(*args)
|
1125
|
+
|
1126
|
+
return result
|
1127
|
+
|
1128
|
+
|
1129
|
+
class Drowning_victim_Viewer(Element_To_Draw):
|
1130
|
+
|
1131
|
+
def __init__(self, idx = '', plotted = True, mapviewer = None, need_for_wx = False,filedir = None):
|
1132
|
+
super().__init__(idx, plotted, mapviewer, need_for_wx)
|
1133
|
+
|
1134
|
+
self.filename = None
|
1135
|
+
self.filedir = filedir
|
1136
|
+
self.file_drowning = None
|
1137
|
+
self.n_peaks = 2
|
1138
|
+
self.init_plot()
|
1139
|
+
|
1140
|
+
self.newdrowning = Drowning_victim(Path_dir=filedir)
|
1141
|
+
|
1142
|
+
self.from_dictionnary_to_wp()
|
1143
|
+
|
1144
|
+
def selection_drowning_point(self,event):
|
1145
|
+
"""
|
1146
|
+
Function to select the drowning point in the viewer
|
1147
|
+
"""
|
1148
|
+
from ..PyDraw import draw_type
|
1149
|
+
liste = self.mapviewer.get_list_keys(draw_type.RES2D,checked_state=None)
|
1150
|
+
|
1151
|
+
if not liste:
|
1152
|
+
dialog = wx.DirDialog(None, "Folder containing the wanted simulation WOLF 2D GPU", style=wx.DD_DEFAULT_STYLE)
|
1153
|
+
|
1154
|
+
# Afficher la boîte de dialogue et attendre l'interaction de l'utilisateur
|
1155
|
+
if dialog.ShowModal() == wx.ID_OK:
|
1156
|
+
# Récupérer le chemin sélectionné
|
1157
|
+
self.newdrowning.Path_Wolf = dialog.GetPath()
|
1158
|
+
self.wp.change_param('Paths','Results of Wolf GPU simulation path',self.newdrowning.Path_Wolf)
|
1159
|
+
|
1160
|
+
# Ajouter l'objet avec le chemin sélectionné
|
1161
|
+
self.mapviewer.add_object(which='res2d_gpu', ToCheck=True, filename=join(self.newdrowning.Path_Wolf, 'Results'))
|
1162
|
+
self.mapviewer.menu_wolf2d()
|
1163
|
+
self.mapviewer.menu_2dgpu()
|
1164
|
+
self.mapviewer.Autoscale()
|
1165
|
+
dialog.Destroy()
|
1166
|
+
else:
|
1167
|
+
# L'utilisateur a annulé la boîte de dialogue
|
1168
|
+
logging.info(_('No folder selected for the WOLF 2D GPU simulation.'))
|
1169
|
+
# Détruire la boîte de dialogue pour libérer les ressources
|
1170
|
+
dialog.Destroy()
|
1171
|
+
return
|
1172
|
+
|
1173
|
+
else:
|
1174
|
+
myitem = self.mapviewer.single_choice_key(draw_type.RES2D,checked_state=None)
|
1175
|
+
# nameitem = self.mapviewer.treelist.GetItemText(myitem).lower()
|
1176
|
+
# curobj = self.mapviewer.getobj_from_id(nameitem)
|
1177
|
+
# myobj = self.mapviewer.treelist.GetItemData(myitem)
|
1178
|
+
# self.mapviewer.active_res2d = myobj
|
1179
|
+
|
1180
|
+
self.wp.change_param('Grid','Origin X',self.mapviewer.active_res2d.origx)
|
1181
|
+
self.wp.change_param('Grid','Origin Y',self.mapviewer.active_res2d.origy)
|
1182
|
+
|
1183
|
+
with open(join(self.newdrowning.Path_Wolf,'parameters.json'), 'r', encoding='utf-8') as file:
|
1184
|
+
data = json.load(file)
|
1185
|
+
dx = data['parameters']['dx']
|
1186
|
+
dy = data['parameters']['dy']
|
1187
|
+
nbx = data['parameters']['nbx']
|
1188
|
+
nby = data['parameters']['nby']
|
1189
|
+
self.wp.change_param('Grid','Delta X',dx)
|
1190
|
+
self.wp.change_param('Grid','Delta Y',dy)
|
1191
|
+
self.wp.change_param('Grid','Nb X',nbx)
|
1192
|
+
self.wp.change_param('Grid','Nb Y',nby)
|
1193
|
+
|
1194
|
+
self.button_selection_progress = wx.Button(self.wp,label='Drowning point')
|
1195
|
+
self.button_selection_progress.Bind(wx.EVT_BUTTON,self.selection_progress)
|
1196
|
+
self.button_selection_progress.SetToolTip('Check if you have exactly one drowning point selected')
|
1197
|
+
self.wp.sizerbut.Insert(4,self.button_selection_progress,1,wx.EXPAND)
|
1198
|
+
self.wp.sizer.Fit(self.wp)
|
1199
|
+
|
1200
|
+
self.wp.SetSize(0,0,self.w,800)
|
1201
|
+
self.wp.Show(True)
|
1202
|
+
|
1203
|
+
def selection_progress(self,event):
|
1204
|
+
"""
|
1205
|
+
Function to select the drowning point in the viewer
|
1206
|
+
"""
|
1207
|
+
|
1208
|
+
if self.mapviewer.active_res2d.SelectionData.nb==0:
|
1209
|
+
wx.MessageBox(_("No point selected, please select a drowning point"), "Error", wx.OK | wx.ICON_ERROR)
|
1210
|
+
return
|
1211
|
+
|
1212
|
+
elif self.mapviewer.active_res2d.SelectionData.nb==1:
|
1213
|
+
value_got = self.mapviewer.active_res2d.myblocks[getkeyblock(0)].SelectionData.myselection
|
1214
|
+
x,y = value_got[0]
|
1215
|
+
self.newdrowning.ind_pos_0_x, self.newdrowning.ind_pos_0_y = self.mapviewer.active_res2d.get_ij_from_xy(x=x,y=y)
|
1216
|
+
self.update_drowning_pos()
|
1217
|
+
self.mapviewer.active_res2d.SelectionData.reset_all()
|
1218
|
+
self.button_selection_progress.SetBackgroundColour(wx.Colour(50, 190, 50))
|
1219
|
+
self.file_drowning = 1
|
1220
|
+
return
|
1221
|
+
|
1222
|
+
elif self.mapviewer.active_res2d.SelectionData.nb>1:
|
1223
|
+
wx.MessageBox(_("More than one point selected, please select only one drowning point"), "Error", wx.OK | wx.ICON_ERROR)
|
1224
|
+
return
|
1225
|
+
|
1226
|
+
def update_drowning_pos(self):
|
1227
|
+
"""
|
1228
|
+
Update the values of "X-cell" and "Y-cell" in the parameters.param file.
|
1229
|
+
"""
|
1230
|
+
|
1231
|
+
self.wp.change_param("Initial_drowning_point",'X-cell', int(self.newdrowning.ind_pos_0_x))
|
1232
|
+
self.wp.change_param("Initial_drowning_point",'Y-cell', int(self.newdrowning.ind_pos_0_y))
|
1233
|
+
|
1234
|
+
def create_exe_file(self,event):
|
1235
|
+
"""
|
1236
|
+
Start the drowning in a separate process
|
1237
|
+
"""
|
1238
|
+
import subprocess
|
1239
|
+
try:
|
1240
|
+
if self.filedir is None:
|
1241
|
+
wx.MessageBox(_("No directory selected for the simulation. \nPlease, save your drowning in a directory."), "Error", wx.OK | wx.ICON_ERROR)
|
1242
|
+
return
|
1243
|
+
|
1244
|
+
self.filedir = Path(self.filedir)
|
1245
|
+
# Créer le répertoire self.Path_saving s'il n'existe pas
|
1246
|
+
self.filedir.mkdir(parents=True, exist_ok=True)
|
1247
|
+
# Définir le chemin du fichier exe_drowning.py
|
1248
|
+
self.exe_file = self.filedir / "exe_drowning.py"
|
1249
|
+
project_root = Path(__file__).resolve().parents[2]
|
1250
|
+
# Contenu du fichier exe_drowning.py
|
1251
|
+
script_content = f"""
|
1252
|
+
import sys
|
1253
|
+
import os
|
1254
|
+
from pathlib import Path
|
1255
|
+
|
1256
|
+
directory = r"{project_root}"
|
1257
|
+
os.chdir(directory)
|
1258
|
+
|
1259
|
+
_root_dir = os.path.dirname(os.path.realpath(__file__))
|
1260
|
+
sys.path.insert(0,os.path.join(directory,'./wolfhece'))
|
1261
|
+
|
1262
|
+
try:
|
1263
|
+
from .Drowning_victims.Class import Drowning_victim
|
1264
|
+
except:
|
1265
|
+
from Drowning_victims.Class import Drowning_victim
|
1266
|
+
|
1267
|
+
if __name__ == "__main__":
|
1268
|
+
# Définir le chemin de sauvegarde
|
1269
|
+
Path_saving = r"{self.filedir}"
|
1270
|
+
|
1271
|
+
# Exécuter la simulation
|
1272
|
+
newdrowning = Drowning_victim(Path_dir=Path_saving)
|
1273
|
+
newdrowning.start()
|
1274
|
+
"""
|
1275
|
+
|
1276
|
+
# Créer et écrire le fichier exe_drowning.py
|
1277
|
+
with open(self.exe_file, "w", encoding="utf-8") as file:
|
1278
|
+
file.write(script_content)
|
1279
|
+
logging.info(f"Drowning simulation file created at: {self.exe_file}")
|
1280
|
+
except subprocess.CalledProcessError as e:
|
1281
|
+
logging.error(f"Error while creating the drowning simulation file: {e}")
|
1282
|
+
|
1283
|
+
def run_code(self,event):
|
1284
|
+
if self.file_drowning is not None:
|
1285
|
+
import subprocess
|
1286
|
+
try:
|
1287
|
+
self.newdrowning.start()
|
1288
|
+
logging.info(_("Drowning simulation done."))
|
1289
|
+
except subprocess.CalledProcessError as e:
|
1290
|
+
logging.error(f"Error while running the drowning simulation file: {e}")
|
1291
|
+
# process = multiprocessing.Process(target=self.__main__())
|
1292
|
+
# process.start() # Démarre code2 dans un processus distinct
|
1293
|
+
else:
|
1294
|
+
logging.error(('No drowning point selected, select one before starting the simulation'))
|
1295
|
+
|
1296
|
+
def show_properties(self):
|
1297
|
+
|
1298
|
+
self.w=800
|
1299
|
+
|
1300
|
+
self.wp._set_gui(title='Parameters for the drowning simulation', toShow=True, w=self.w)
|
1301
|
+
self.wp.hide_selected_buttons([Buttons.Reload,Buttons.Save])
|
1302
|
+
|
1303
|
+
select_button = wx.Button(self.wp,id=10,label="Wolf2D simulation")
|
1304
|
+
select_button.SetToolTip(_("Select reference Wolf2D simulation to choose your drowning point"))
|
1305
|
+
create_file_button = wx.Button(self.wp,id=11,label="Create exe file")
|
1306
|
+
create_file_button.SetToolTip(_("Create the executable file to run the drowning"))
|
1307
|
+
run_button = wx.Button(self.wp,id=12,label="Run")
|
1308
|
+
run_button.SetToolTip(_("Run the drowning simulation \nNot recommended here"))
|
1309
|
+
|
1310
|
+
select_button.Bind(wx.EVT_BUTTON, self.selection_drowning_point)
|
1311
|
+
create_file_button.Bind(wx.EVT_BUTTON, self.create_exe_file)
|
1312
|
+
run_button.Bind(wx.EVT_BUTTON, self.run_code)
|
1313
|
+
run_button.SetBackgroundColour(wx.Colour(240,160,160))
|
1314
|
+
|
1315
|
+
self.wp.sizerbut.Add(select_button,2,wx.EXPAND)
|
1316
|
+
self.wp.sizerbut.Add(create_file_button,2,wx.EXPAND)
|
1317
|
+
self.wp.sizerbut.Add(run_button,1,wx.EXPAND)
|
1318
|
+
|
1319
|
+
|
1320
|
+
self.wp.SetSizer(self.wp.sizer)
|
1321
|
+
# self.SetSize(w,h)
|
1322
|
+
self.wp.SetAutoLayout(1)
|
1323
|
+
self.wp.sizer.Fit(self.wp)
|
1324
|
+
|
1325
|
+
self.wp.SetSize(0,0,self.w,800)
|
1326
|
+
self.wp.Show(True)
|
1327
|
+
|
1328
|
+
# self.wp.myparams = self.merge_dicts(self.wp.myparams,self.wp.myparams_default)
|
1329
|
+
self.from_wp_to_dictionnary()
|
1330
|
+
self.newdrowning.from_dictionnary_to_attributes()
|
1331
|
+
|
1332
|
+
def from_dictionnary_to_wp(self):
|
1333
|
+
"""
|
1334
|
+
Return a Wolf_Param object that represents the parameters of the simulation,
|
1335
|
+
directly using the attributes of the class.
|
1336
|
+
"""
|
1337
|
+
# Initialisation de l'objet Wolf_Param
|
1338
|
+
wp = Wolf_Param(
|
1339
|
+
parent=None,
|
1340
|
+
title="Drift of a drowning victim",
|
1341
|
+
to_read=False,
|
1342
|
+
withbuttons=True,
|
1343
|
+
toShow=False,
|
1344
|
+
init_GUI=False,
|
1345
|
+
force_even_if_same_default=True,
|
1346
|
+
filename=self.filename
|
1347
|
+
)
|
1348
|
+
|
1349
|
+
params_dict = self.newdrowning.param_dict
|
1350
|
+
|
1351
|
+
# Ajout des paramètres au Wolf_Param
|
1352
|
+
for current_section in params_dict.keys():
|
1353
|
+
for key in params_dict[current_section].keys():
|
1354
|
+
|
1355
|
+
value = params_dict[current_section][key]["value"]
|
1356
|
+
description = params_dict[current_section][key]["description"]
|
1357
|
+
name = params_dict[current_section][key]["explicit name"]
|
1358
|
+
|
1359
|
+
wp.add_param(
|
1360
|
+
groupname=current_section,
|
1361
|
+
name=name,
|
1362
|
+
value=value,
|
1363
|
+
type=params_dict[current_section][key]["type"],
|
1364
|
+
whichdict="All" if params_dict[current_section][key]["mandatory"] else "Default",
|
1365
|
+
jsonstr={"Values": params_dict[current_section][key]["choices"]} if params_dict[current_section][key]["choices"] else None,
|
1366
|
+
comment= description
|
1367
|
+
)
|
1368
|
+
self.wp = wp
|
1369
|
+
|
1370
|
+
self.newdrowning.time_goal = self.newdrowning.Days*24*60*60 + self.newdrowning.Hours*60*60 + self.newdrowning.Minutes*60 + self.newdrowning.Seconds #s
|
1371
|
+
wanted_time = np.array([self.newdrowning.t_initial])
|
1372
|
+
self.newdrowning.n_saved = 1
|
1373
|
+
for i in np.arange(10,self.newdrowning.time_goal+10,10):
|
1374
|
+
if i<60:
|
1375
|
+
wanted_time = np.append(wanted_time,i)
|
1376
|
+
self.newdrowning.n_saved += 1
|
1377
|
+
elif np.logical_and(i<10*60,(i%60==0)):
|
1378
|
+
wanted_time = np.append(wanted_time,i)
|
1379
|
+
self.newdrowning.n_saved += 1
|
1380
|
+
elif np.logical_and(i<30*60,(i%(5*60)==0)):
|
1381
|
+
wanted_time = np.append(wanted_time,i)
|
1382
|
+
self.newdrowning.n_saved += 1
|
1383
|
+
elif np.logical_and(i<60*60,(i%(10*60)==0)):
|
1384
|
+
wanted_time = np.append(wanted_time,i)
|
1385
|
+
self.newdrowning.n_saved += 1
|
1386
|
+
elif np.logical_and(i<=24*60*60,(i%(60*60)==0)):
|
1387
|
+
wanted_time = np.append(wanted_time,i)
|
1388
|
+
self.newdrowning.n_saved += 1
|
1389
|
+
elif np.logical_and(i<=2*24*60*60,(i%(2*60*60)==0)):
|
1390
|
+
wanted_time = np.append(wanted_time,i)
|
1391
|
+
self.newdrowning.n_saved += 1
|
1392
|
+
elif (i%(3*60*60)==0):
|
1393
|
+
wanted_time = np.append(wanted_time,i)
|
1394
|
+
self.newdrowning.n_saved += 1
|
1395
|
+
|
1396
|
+
self.newdrowning.wanted_time = np.append(wanted_time,0)
|
1397
|
+
|
1398
|
+
def from_wp_to_dictionnary(self):
|
1399
|
+
"""
|
1400
|
+
Compare the parameters in self.wp with self.newdrowning.param_dict and update the values
|
1401
|
+
in self.newdrowning.param_dict when they differ.
|
1402
|
+
"""
|
1403
|
+
# Charger le dictionnaire existant
|
1404
|
+
param_dict = self.newdrowning.param_dict
|
1405
|
+
|
1406
|
+
# Parcourir les sections et les clés de wp
|
1407
|
+
for current_section in self.wp.myparams.keys():
|
1408
|
+
if current_section in param_dict:
|
1409
|
+
for key, wp_param in self.wp.myparams[current_section].items():
|
1410
|
+
# Trouver la clé correspondante dans param_dict
|
1411
|
+
for param_key, param_data in param_dict[current_section].items():
|
1412
|
+
if param_data["explicit name"] == key:
|
1413
|
+
# Comparer les valeurs et mettre à jour si elles diffèrent
|
1414
|
+
if param_data["value"] != wp_param[key_Param.VALUE]:
|
1415
|
+
param_dict[current_section][param_key]["value"] = wp_param[key_Param.VALUE]
|
1416
|
+
break
|
1417
|
+
|
1418
|
+
# Sauvegarder le dictionnaire mis à jour dans self.newdrowning.param_dict
|
1419
|
+
self.newdrowning.param_dict = param_dict
|
1420
|
+
|
1421
|
+
def hide_properties(self):
|
1422
|
+
"""
|
1423
|
+
Hide properties window
|
1424
|
+
"""
|
1425
|
+
if self.wp is not None:
|
1426
|
+
self.wp.Destroy()
|
1427
|
+
self.wp = None
|
1428
|
+
|
1429
|
+
def save(self):
|
1430
|
+
'''
|
1431
|
+
Save the parameters in a text file
|
1432
|
+
'''
|
1433
|
+
if self.filename is None:
|
1434
|
+
self.saveas()
|
1435
|
+
|
1436
|
+
else:
|
1437
|
+
self.wp.Save(self.filename)
|
1438
|
+
|
1439
|
+
def saveas(self):
|
1440
|
+
'''
|
1441
|
+
Save the parameters in a text file
|
1442
|
+
'''
|
1443
|
+
fdlg = wx.DirDialog(None, "Where should the parameters be stored? File automatically named parameters", style=wx.FD_SAVE)
|
1444
|
+
ret = fdlg.ShowModal()
|
1445
|
+
if ret == wx.ID_OK:
|
1446
|
+
self.filedir = fdlg.GetPath()
|
1447
|
+
self.filename = self.filedir + "/parameters.param"
|
1448
|
+
self.Path_saving = self.filedir
|
1449
|
+
self.wp.change_param("Paths","Save path",self.filedir)
|
1450
|
+
self.save()
|
1451
|
+
|
1452
|
+
fdlg.Destroy()
|
1453
|
+
|
1454
|
+
def load_results(self):
|
1455
|
+
"""
|
1456
|
+
Load the results from the 'Results.npz' file and assign the arrays as attributes of the class.
|
1457
|
+
"""
|
1458
|
+
|
1459
|
+
# Construire le chemin du fichier Results.npz
|
1460
|
+
results_file = join(self.filedir, 'Results.npz')
|
1461
|
+
|
1462
|
+
# Vérifier si le fichier existe
|
1463
|
+
if not os.path.exists(results_file):
|
1464
|
+
logging.error(f"Le fichier {results_file} est introuvable.")
|
1465
|
+
return
|
1466
|
+
|
1467
|
+
# Charger le fichier npz
|
1468
|
+
with np.load(results_file, allow_pickle=True) as data:
|
1469
|
+
# Assigner les tableaux comme attributs de la classe
|
1470
|
+
self.Human = data['Human']
|
1471
|
+
self.Pos_b = data['Pos_b']
|
1472
|
+
self.U_b = data['U_b']
|
1473
|
+
self.Z_param = data['Z_param']
|
1474
|
+
self.wanted_time = data['wanted_time']
|
1475
|
+
self.time_b = data['time_b']
|
1476
|
+
|
1477
|
+
self.Pos_b[:,0,:] += self.newdrowning.origx
|
1478
|
+
self.Pos_b[:,1,:] += self.newdrowning.origy
|
1479
|
+
|
1480
|
+
def init_plot(self):
|
1481
|
+
|
1482
|
+
self.bottom_cells = None
|
1483
|
+
self.bottom_kde = None
|
1484
|
+
self.vertex_bottom_run = None
|
1485
|
+
|
1486
|
+
self.surface_cells = None
|
1487
|
+
self.surface_kde = None
|
1488
|
+
self.vertex_surface_run = None
|
1489
|
+
|
1490
|
+
self.plot_runs = None
|
1491
|
+
self.plot_cells = None
|
1492
|
+
self.plot_KDE = None
|
1493
|
+
|
1494
|
+
def read_oneresult(self,idx):
|
1495
|
+
"""
|
1496
|
+
Read one result of the simulation and update the parameters in the GUI
|
1497
|
+
"""
|
1498
|
+
|
1499
|
+
count=0
|
1500
|
+
|
1501
|
+
self.time_id = idx
|
1502
|
+
|
1503
|
+
if self.plot_runs is not None:
|
1504
|
+
self.prepare_plot_runs_positions()
|
1505
|
+
count +=1
|
1506
|
+
if self.plot_cells is not None:
|
1507
|
+
self.prepare_plot_cells_positions()
|
1508
|
+
count +=1
|
1509
|
+
if self.plot_KDE is not None:
|
1510
|
+
self.prepare_plot_kde()
|
1511
|
+
count +=1
|
1512
|
+
|
1513
|
+
if count==0:
|
1514
|
+
self.prepare_plot_runs_positions()
|
1515
|
+
|
1516
|
+
return
|
1517
|
+
|
1518
|
+
def read_last_result(self):
|
1519
|
+
"""
|
1520
|
+
Read the last results of the simulation and update the parameters in the GUI
|
1521
|
+
"""
|
1522
|
+
|
1523
|
+
self.time_id = -1
|
1524
|
+
|
1525
|
+
self.read_oneresult(idx=-1)
|
1526
|
+
|
1527
|
+
return
|
1528
|
+
|
1529
|
+
def find_minmax(self, update=False):
|
1530
|
+
"""
|
1531
|
+
Generic function to find min and max spatial extent in data
|
1532
|
+
|
1533
|
+
example : a WolfMapViewer instance needs spatial extent to zoom or test if
|
1534
|
+
element must be plotted
|
1535
|
+
"""
|
1536
|
+
|
1537
|
+
self.xmin=999999. # spatial extension - lower left corner X
|
1538
|
+
self.ymin=999999. # spatial extension - lower left corner Y
|
1539
|
+
self.xmax=-999999. # spatial extension - upper right corner X
|
1540
|
+
self.ymax=-999999. # spatial extension - upper right corner Y
|
1541
|
+
|
1542
|
+
if self.bottom_kde is not None:
|
1543
|
+
[xmin, xmax], [ymin, ymax] = self.bottom_kde.get_bounds()
|
1544
|
+
self.xmin = min(self.xmin, xmin)
|
1545
|
+
self.xmax = max(self.xmax, xmax)
|
1546
|
+
self.ymin = min(self.ymin, ymin)
|
1547
|
+
self.ymax = max(self.ymax, ymax)
|
1548
|
+
if self.surface_kde is not None:
|
1549
|
+
[xmin, xmax], [ymin, ymax] = self.surface_kde.get_bounds()
|
1550
|
+
self.xmin = min(self.xmin, xmin)
|
1551
|
+
self.xmax = max(self.xmax, xmax)
|
1552
|
+
self.ymin = min(self.ymin, ymin)
|
1553
|
+
self.ymax = max(self.ymax, ymax)
|
1554
|
+
|
1555
|
+
if self.bottom_cells is not None:
|
1556
|
+
[xmin, xmax], [ymin, ymax] = self.bottom_cells.get_bounds()
|
1557
|
+
self.xmin = min(self.xmin, xmin)
|
1558
|
+
self.xmax = max(self.xmax, xmax)
|
1559
|
+
self.ymin = min(self.ymin, ymin)
|
1560
|
+
self.ymax = max(self.ymax, ymax)
|
1561
|
+
if self.surface_cells is not None:
|
1562
|
+
[xmin, xmax], [ymin, ymax] = self.surface_cells.get_bounds()
|
1563
|
+
self.xmin = min(self.xmin, xmin)
|
1564
|
+
self.xmax = max(self.xmax, xmax)
|
1565
|
+
self.ymin = min(self.ymin, ymin)
|
1566
|
+
self.ymax = max(self.ymax, ymax)
|
1567
|
+
|
1568
|
+
if self.vertex_bottom_run is not None:
|
1569
|
+
self.vertex_bottom_run.find_minmax(update)
|
1570
|
+
self.xmin = min(self.xmin, self.vertex_bottom_run.xmin)
|
1571
|
+
self.xmax = max(self.xmax, self.vertex_bottom_run.xmax)
|
1572
|
+
self.ymin = min(self.ymin, self.vertex_bottom_run.ymin)
|
1573
|
+
self.ymax = max(self.ymax, self.vertex_bottom_run.ymax)
|
1574
|
+
if self.vertex_surface_run is not None:
|
1575
|
+
self.vertex_surface_run.find_minmax(update)
|
1576
|
+
self.xmin = min(self.xmin, self.vertex_surface_run.xmin)
|
1577
|
+
self.xmax = max(self.xmax, self.vertex_surface_run.xmax)
|
1578
|
+
self.ymin = min(self.ymin, self.vertex_surface_run.ymin)
|
1579
|
+
self.ymax = max(self.ymax, self.vertex_surface_run.ymax)
|
1580
|
+
|
1581
|
+
pass
|
1582
|
+
|
1583
|
+
def plot(self, sx=None, sy=None, xmin=None, ymin=None, xmax=None, ymax=None, size=None):
|
1584
|
+
"""
|
1585
|
+
Plot data in OpenGL context
|
1586
|
+
"""
|
1587
|
+
if self.plotted:
|
1588
|
+
|
1589
|
+
self.plotting = True
|
1590
|
+
|
1591
|
+
if self.bottom_kde is not None:
|
1592
|
+
self.bottom_kde.check_plot()
|
1593
|
+
self.bottom_kde.plot(sx, sy, xmin, ymin, xmax, ymax, size)
|
1594
|
+
if self.surface_kde is not None:
|
1595
|
+
self.surface_kde.check_plot()
|
1596
|
+
self.surface_kde.plot(sx, sy, xmin, ymin, xmax, ymax, size)
|
1597
|
+
|
1598
|
+
if self.bottom_cells is not None:
|
1599
|
+
self.bottom_cells.check_plot()
|
1600
|
+
self.bottom_cells.plot(sx, sy, xmin, ymin, xmax, ymax, size)
|
1601
|
+
if self.surface_cells is not None:
|
1602
|
+
self.surface_cells.check_plot()
|
1603
|
+
self.surface_cells.plot(sx, sy, xmin, ymin, xmax, ymax, size)
|
1604
|
+
|
1605
|
+
if self.vertex_bottom_run is not None:
|
1606
|
+
self.vertex_bottom_run.plot()
|
1607
|
+
if self.vertex_surface_run is not None:
|
1608
|
+
self.vertex_surface_run.plot()
|
1609
|
+
|
1610
|
+
self.plotting = False
|
1611
|
+
|
1612
|
+
def sort_positions_bodies(self):
|
1613
|
+
|
1614
|
+
time_id = self.time_id
|
1615
|
+
|
1616
|
+
ind_surface = np.where(self.Pos_b[:,2,time_id] > 0.2)[0]
|
1617
|
+
ind_bottom = np.where(self.Pos_b[:,2,time_id] <= 0.2)[0]
|
1618
|
+
if len(ind_surface) == 0:
|
1619
|
+
ind_surface = [0]
|
1620
|
+
if len(ind_bottom) == 0:
|
1621
|
+
ind_bottom = [0]
|
1622
|
+
|
1623
|
+
return ind_bottom,ind_surface,time_id
|
1624
|
+
|
1625
|
+
def prepare_plot_runs_positions(self):
|
1626
|
+
"""
|
1627
|
+
Plot the runs position on a georeferenced map with bodies in blue being at the bottom and red being at the surface.
|
1628
|
+
"""
|
1629
|
+
|
1630
|
+
self.plot_runs = 1
|
1631
|
+
|
1632
|
+
ind_bottom,ind_surface,time_id = self.sort_positions_bodies()
|
1633
|
+
|
1634
|
+
self.vertex_bottom_run = cloud_vertices(mapviewer=self.mapviewer)
|
1635
|
+
self.vertex_surface_run = cloud_vertices(mapviewer=self.mapviewer)
|
1636
|
+
|
1637
|
+
self.vertex_bottom_run.init_from_nparray(self.Pos_b[ind_bottom,:,time_id])
|
1638
|
+
self.vertex_surface_run.init_from_nparray(self.Pos_b[ind_surface,:,time_id])
|
1639
|
+
|
1640
|
+
self.vertex_bottom_run.myprop.color = [40,50,250]
|
1641
|
+
self.vertex_surface_run.myprop.color = [250,100,80]
|
1642
|
+
|
1643
|
+
self.vertex_bottom_run.myprop.alpha = 0.5
|
1644
|
+
self.vertex_surface_run.myprop.alpha = 0.5
|
1645
|
+
|
1646
|
+
self.find_minmax(True)
|
1647
|
+
|
1648
|
+
return
|
1649
|
+
|
1650
|
+
def reset_plot_runs_positions(self):
|
1651
|
+
self.vertex_bottom_run = None
|
1652
|
+
self.vertex_surface_run = None
|
1653
|
+
self.plot_runs = None
|
1654
|
+
|
1655
|
+
def kde_on_grid(self,points, bandwidth, xmin, xmax, ymin, ymax, grid_size):
|
1656
|
+
"""
|
1657
|
+
Function used to calculate the kde on a point cloud. Use a large grid size to identify peaks and a small one to refine
|
1658
|
+
"""
|
1659
|
+
|
1660
|
+
|
1661
|
+
x_grid = np.linspace(xmin, xmax, grid_size[0])
|
1662
|
+
y_grid = np.linspace(ymin, ymax, grid_size[1])
|
1663
|
+
X, Y = np.meshgrid(x_grid, y_grid)
|
1664
|
+
sample_grid = np.vstack([X.ravel(), Y.ravel()]).T
|
1665
|
+
|
1666
|
+
kde = KernelDensity(bandwidth=bandwidth)
|
1667
|
+
kde.fit(points)
|
1668
|
+
Z = np.exp(kde.score_samples(sample_grid)).reshape(grid_size[0], grid_size[1])
|
1669
|
+
|
1670
|
+
return Z, x_grid, y_grid
|
1671
|
+
|
1672
|
+
def detect_peaks(self,x,y,radius,num_peaks=2):
|
1673
|
+
"""
|
1674
|
+
Détecte les pics locaux dans une matrice 2D sans skimage.
|
1675
|
+
|
1676
|
+
param: x X coordinate of the points cloud
|
1677
|
+
param: y Y coordinate of the points cloud
|
1678
|
+
param: radius size of the grid to detect peaks
|
1679
|
+
param: n_peaks number of peaks to detect
|
1680
|
+
|
1681
|
+
"""
|
1682
|
+
|
1683
|
+
x += -self.newdrowning.origx
|
1684
|
+
y += -self.newdrowning.origy
|
1685
|
+
|
1686
|
+
dx = self.newdrowning.dx
|
1687
|
+
dy = self.newdrowning.dy
|
1688
|
+
|
1689
|
+
ij = np.array([np.int32(x/radius), np.int32(y/radius)]).T
|
1690
|
+
unique_positions, counts = np.unique(ij,axis=0, return_counts=True)
|
1691
|
+
|
1692
|
+
ind_peaks = []
|
1693
|
+
selected_mask = np.zeros(len(counts), dtype=bool) # pour marquer les indices déjà exclus
|
1694
|
+
|
1695
|
+
while True:
|
1696
|
+
# On masque les indices déjà exclus
|
1697
|
+
valid_indices = np.where(~selected_mask)[0]
|
1698
|
+
if len(valid_indices) == 0:
|
1699
|
+
break
|
1700
|
+
|
1701
|
+
# Trouver le max parmi les valides
|
1702
|
+
idx_max = valid_indices[np.argmax(counts[valid_indices])]
|
1703
|
+
ind_peaks.append(idx_max)
|
1704
|
+
|
1705
|
+
# Marquer les indices trop proches pour les exclure ensuite
|
1706
|
+
pos_max = unique_positions[idx_max]
|
1707
|
+
i_diff = np.abs(unique_positions[:, 0] - pos_max[0])
|
1708
|
+
j_diff = np.abs(unique_positions[:, 1] - pos_max[1])
|
1709
|
+
too_close = (i_diff <= 0) & (j_diff <= 0)
|
1710
|
+
|
1711
|
+
selected_mask |= too_close # mettre à jour le masque d'exclusion
|
1712
|
+
|
1713
|
+
if len(ind_peaks) >= num_peaks:
|
1714
|
+
break
|
1715
|
+
|
1716
|
+
x_peaks = (unique_positions[ind_peaks,0]*radius) + radius/2 + self.newdrowning.origx
|
1717
|
+
y_peaks = (unique_positions[ind_peaks,1]*radius) + radius/2 + self.newdrowning.origy
|
1718
|
+
|
1719
|
+
selected_peaks = np.zeros((len(ind_peaks),2))
|
1720
|
+
selected_peaks[:,0] = x_peaks
|
1721
|
+
selected_peaks[:,1] = y_peaks
|
1722
|
+
|
1723
|
+
return selected_peaks
|
1724
|
+
|
1725
|
+
def kde_refined_based_coarse(self, points, wolfarray, bandwidth=50,
|
1726
|
+
coarse_grid_size=50, fine_grid_size=5,
|
1727
|
+
window_size=200, radius=150, n_peaks=3):
|
1728
|
+
"""
|
1729
|
+
Optimisation à 2 étages : détection des pics sur grille grossière puis raffinement local.
|
1730
|
+
|
1731
|
+
Returns:
|
1732
|
+
- refined_peaks : coordonnées des pics raffinés
|
1733
|
+
- clusters : liste de points pour chaque cluster
|
1734
|
+
- coords : coordonnées (x, y) de chaque maille dans les zones raffinées
|
1735
|
+
- values : valeur KDE associée à chaque maille
|
1736
|
+
"""
|
1737
|
+
array = wolfarray.array[:,:]
|
1738
|
+
|
1739
|
+
x_min, y_min = points.min(axis=0)
|
1740
|
+
x_max, y_max = points.max(axis=0)
|
1741
|
+
|
1742
|
+
# 1. Déduction de dx, dy depuis array
|
1743
|
+
ny, nx = array.shape
|
1744
|
+
dx = (x_max - x_min) / nx
|
1745
|
+
dy = (y_max - y_min) / ny
|
1746
|
+
|
1747
|
+
# 3. Préparation des résultats
|
1748
|
+
all_indices = []
|
1749
|
+
all_values = []
|
1750
|
+
|
1751
|
+
ij = wolfarray.get_ij_from_xy_array(points)
|
1752
|
+
unique_positions, counts = np.unique(ij,axis=0, return_counts=True)
|
1753
|
+
delta = radius/self.newdrowning.dx
|
1754
|
+
|
1755
|
+
ind_peaks = []
|
1756
|
+
selected_mask = np.zeros(len(counts), dtype=bool) # pour marquer les indices déjà exclus
|
1757
|
+
|
1758
|
+
while True:
|
1759
|
+
# On masque les indices déjà exclus
|
1760
|
+
valid_indices = np.where(~selected_mask)[0]
|
1761
|
+
if len(valid_indices) == 0:
|
1762
|
+
break
|
1763
|
+
|
1764
|
+
# Trouver le max parmi les valides
|
1765
|
+
idx_max = valid_indices[np.argmax(counts[valid_indices])]
|
1766
|
+
ind_peaks.append(idx_max)
|
1767
|
+
|
1768
|
+
# Marquer les indices trop proches pour les exclure ensuite
|
1769
|
+
pos_max = unique_positions[idx_max]
|
1770
|
+
i_diff = np.abs(unique_positions[:, 0] - pos_max[0])
|
1771
|
+
j_diff = np.abs(unique_positions[:, 1] - pos_max[1])
|
1772
|
+
too_close = (i_diff <= delta) & (j_diff <= delta)
|
1773
|
+
|
1774
|
+
selected_mask |= too_close # mettre à jour le masque d'exclusion
|
1775
|
+
|
1776
|
+
if len(ind_peaks) >= n_peaks:
|
1777
|
+
break
|
1778
|
+
|
1779
|
+
for ind_peak in ind_peaks:
|
1780
|
+
|
1781
|
+
x0 = int((unique_positions[ind_peak,0]-delta)*dx+x_min)
|
1782
|
+
x1 = int((unique_positions[ind_peak,0]+delta)*dx+x_min)
|
1783
|
+
y0 = int((unique_positions[ind_peak,1]-delta)*dy+y_min)
|
1784
|
+
y1 = int((unique_positions[ind_peak,1]+delta)*dy+y_min)
|
1785
|
+
|
1786
|
+
grid_nx = max(2, int((x1 - x0) / dx))
|
1787
|
+
grid_ny = max(2, int((y1 - y0) / dy))
|
1788
|
+
|
1789
|
+
Z_fine, xg_fine, yg_fine = self.kde_on_grid(
|
1790
|
+
points, bandwidth, x0, x1, y0, y1, grid_size=(grid_nx, grid_ny)
|
1791
|
+
)
|
1792
|
+
|
1793
|
+
# Z_fine, xg_fine, yg_fine = self.kde_on_grid(
|
1794
|
+
# local_points, bandwidth, x0, x1, y0, y1, grid_size=grid
|
1795
|
+
# )
|
1796
|
+
|
1797
|
+
# Coordonnées physiques -> indices dans `array`
|
1798
|
+
Y, X = np.meshgrid(yg_fine, xg_fine, indexing='ij')
|
1799
|
+
x_flat = X.ravel()
|
1800
|
+
y_flat = Y.ravel()
|
1801
|
+
i = np.floor((y_flat - y_min) / dy).astype(int)
|
1802
|
+
j = np.floor((x_flat - x_min) / dx).astype(int)
|
1803
|
+
|
1804
|
+
# Filtrage : indices valides dans array
|
1805
|
+
valid = (i >= 0) & (i < ny) & (j >= 0) & (j < nx)
|
1806
|
+
indices = np.stack((i[valid], j[valid]), axis=1) # shape (n, 2)
|
1807
|
+
values = Z_fine.ravel()[valid]
|
1808
|
+
|
1809
|
+
all_indices.append(indices)
|
1810
|
+
all_values.append(values)
|
1811
|
+
|
1812
|
+
# Empilement final
|
1813
|
+
coords_array = np.vstack(all_indices) if all_indices else np.empty((0, 2), dtype=int)
|
1814
|
+
values_array = np.concatenate(all_values) if all_values else np.array([])
|
1815
|
+
|
1816
|
+
return coords_array,values_array
|
1817
|
+
|
1818
|
+
def prepare_plot_kde(self):
|
1819
|
+
"""
|
1820
|
+
Plot the kernel density estimation of positions on a georeferenced map with bodies in blue being at the bottom and red being at the surface.
|
1821
|
+
"""
|
1822
|
+
|
1823
|
+
self.plot_KDE = 1
|
1824
|
+
|
1825
|
+
self.n_peaks = 2
|
1826
|
+
|
1827
|
+
ind_bottom,ind_surface,time_id = self.sort_positions_bodies()
|
1828
|
+
|
1829
|
+
head = header_wolf()
|
1830
|
+
|
1831
|
+
head.set_origin(self.newdrowning.origx, self.newdrowning.origy)
|
1832
|
+
head.set_resolution(self.newdrowning.dx, self.newdrowning.dy)
|
1833
|
+
head.nbx, head.nby = self.newdrowning.nbx, self.newdrowning.nby
|
1834
|
+
head.set_translation(0., 0.)
|
1835
|
+
|
1836
|
+
self.bottom_kde = WolfArray(mapviewer=self.mapviewer, srcheader= head, nullvalue= 0.)
|
1837
|
+
self.surface_kde = WolfArray(mapviewer=self.mapviewer, srcheader= head, nullvalue= 0.)
|
1838
|
+
|
1839
|
+
for locarray, locind in zip([self.bottom_kde, self.surface_kde],
|
1840
|
+
[ind_bottom, ind_surface]):
|
1841
|
+
|
1842
|
+
xy = self.Pos_b[locind,:2,time_id]
|
1843
|
+
coords,values = self.kde_refined_based_coarse(xy,locarray,bandwidth=50,coarse_grid_size=200,fine_grid_size=5,window_size=50,radius=25,n_peaks=self.n_peaks)
|
1844
|
+
|
1845
|
+
locarray.array[:,:] = 0.
|
1846
|
+
locarray.array[coords[:,1],coords[:,0]] = values
|
1847
|
+
locarray.mask_data(0.)
|
1848
|
+
|
1849
|
+
self.bottom_kde.mypal.defaultblue_minmax(self.bottom_kde.array)
|
1850
|
+
self.surface_kde.mypal.defaultred_minmax(self.surface_kde.array)
|
1851
|
+
|
1852
|
+
self.bottom_kde.reset_plot()
|
1853
|
+
self.surface_kde.reset_plot()
|
1854
|
+
|
1855
|
+
return
|
1856
|
+
|
1857
|
+
def reset_plot_kde(self):
|
1858
|
+
self.bottom_kde = None
|
1859
|
+
self.surface_kde = None
|
1860
|
+
self.plot_KDE = None
|
1861
|
+
|
1862
|
+
def prepare_plot_cells_positions(self):
|
1863
|
+
"""
|
1864
|
+
Plot the cells of the WOLF simulation, with a colorbar associated to the number of elements in the cell
|
1865
|
+
"""
|
1866
|
+
self.plot_cells = 1
|
1867
|
+
|
1868
|
+
ind_bottom,ind_surface,time_id = self.sort_positions_bodies()
|
1869
|
+
|
1870
|
+
head = header_wolf()
|
1871
|
+
|
1872
|
+
head.set_origin(self.newdrowning.origx, self.newdrowning.origy)
|
1873
|
+
head.set_resolution(self.newdrowning.dx, self.newdrowning.dy)
|
1874
|
+
head.nbx, head.nby = self.newdrowning.nbx, self.newdrowning.nby
|
1875
|
+
head.set_translation(0., 0.)
|
1876
|
+
|
1877
|
+
self.bottom_cells = WolfArray(mapviewer=self.mapviewer, srcheader= head, nullvalue= 0.)
|
1878
|
+
self.surface_cells = WolfArray(mapviewer=self.mapviewer, srcheader= head, nullvalue= 0.)
|
1879
|
+
|
1880
|
+
for locarray, locind in zip([self.bottom_cells, self.surface_cells],
|
1881
|
+
[ind_bottom, ind_surface]):
|
1882
|
+
|
1883
|
+
# i_bottom,j_bottom = self.bottom_cells.get_ij_from_xy_array(self.Pos_b[ind_bottom,:2,time_id])
|
1884
|
+
ij = locarray.get_ij_from_xy_array(self.Pos_b[locind,:2,time_id])
|
1885
|
+
|
1886
|
+
unique_positions, counts = np.unique(ij,axis=0, return_counts=True)
|
1887
|
+
|
1888
|
+
locarray.array[:,:] = 0.
|
1889
|
+
locarray.array[unique_positions[:,0],unique_positions[:,1]] = counts/self.newdrowning.n_b*100
|
1890
|
+
locarray.mask_data(0.)
|
1891
|
+
|
1892
|
+
array = self.bottom_cells.array
|
1893
|
+
self.bottom_cells.mypal.nb = 2
|
1894
|
+
self.bottom_cells.mypal.values = np.asarray([np.min(array), np.max(array)], dtype=np.float64)
|
1895
|
+
self.bottom_cells.mypal.colors = np.asarray([[175, 200, 255, 255], [0, 0, 255, 255]], dtype=np.int32)
|
1896
|
+
self.bottom_cells.mypal.colorsflt = np.asarray([[0., 0., 0., 1.], [1., 1., 1., 1.]], dtype=np.float64)
|
1897
|
+
self.bottom_cells.mypal.fill_segmentdata()
|
1898
|
+
|
1899
|
+
array = self.surface_cells.array
|
1900
|
+
self.surface_cells.mypal.nb = 2
|
1901
|
+
self.surface_cells.mypal.values = np.asarray([np.min(array), np.max(array)], dtype=np.float64)
|
1902
|
+
self.surface_cells.mypal.colors = np.asarray([[255, 200, 175, 255], [0, 0, 255, 255]], dtype=np.int32)
|
1903
|
+
self.surface_cells.mypal.colorsflt = np.asarray([[0., 0.2, 0.6, 1.], [1., 1., 1., 1.]], dtype=np.float64)
|
1904
|
+
self.surface_cells.mypal.fill_segmentdata()
|
1905
|
+
|
1906
|
+
self.bottom_cells.reset_plot()
|
1907
|
+
self.surface_cells.reset_plot()
|
1908
|
+
|
1909
|
+
return
|
1910
|
+
|
1911
|
+
def reset_plot_cells_positions(self):
|
1912
|
+
self.bottom_cells = None
|
1913
|
+
self.surface_cells = None
|
1914
|
+
self.plot_cells = None
|
1915
|
+
|
1916
|
+
def zoom_on_hotspots(self,memory_view):
|
1917
|
+
"""
|
1918
|
+
Zoom on the hotspots of the KDE
|
1919
|
+
"""
|
1920
|
+
|
1921
|
+
delta = 150
|
1922
|
+
|
1923
|
+
ind_bottom,ind_surface,time_id = self.sort_positions_bodies()
|
1924
|
+
|
1925
|
+
xy_peaks_bottom = self.detect_peaks(self.Pos_b[ind_bottom,0,time_id], self.Pos_b[ind_bottom,1,time_id], radius=100, num_peaks=self.n_peaks)
|
1926
|
+
xy_peaks_surface = self.detect_peaks(self.Pos_b[ind_surface,0,time_id], self.Pos_b[ind_surface,1,time_id], radius=100, num_peaks=self.n_peaks)
|
1927
|
+
xy_peaks = np.concatenate((xy_peaks_bottom, xy_peaks_surface),axis=0)
|
1928
|
+
|
1929
|
+
names = ['Highest peak at the bottom','Second peak at the bottom','Highest peak at the surface','Second peak at the surface']
|
1930
|
+
|
1931
|
+
for locxy,locname in zip(xy_peaks,names):
|
1932
|
+
xmin = locxy[0] - delta
|
1933
|
+
xmax = locxy[0] + delta
|
1934
|
+
ymin = locxy[1] - delta
|
1935
|
+
ymax = locxy[1] + delta
|
1936
|
+
|
1937
|
+
memory_view.add_view(locname, self.mapviewer.canvaswidth, self.mapviewer.canvasheight, xmin, xmax, ymin, ymax)
|
1938
|
+
|
1939
|
+
return
|
1940
|
+
|
1941
|
+
def get_bodies_characteristics(self):
|
1942
|
+
"""
|
1943
|
+
Plots a table of the Dataframe panda "Human" with the characteristics of each run
|
1944
|
+
"""
|
1945
|
+
|
1946
|
+
if not isinstance(self.Human, pd.DataFrame):
|
1947
|
+
Human = pd.DataFrame(self.Human, columns=COLUMNS_HUMAN)
|
1948
|
+
else:
|
1949
|
+
Human = self.Human
|
1950
|
+
|
1951
|
+
self.grid = PandasGrid(parent=self.mapviewer, id = self.idx, df=Human)
|
1952
|
+
self.grid.ShowModal()
|
1953
|
+
self.grid.Destroy()
|
1954
|
+
|
1955
|
+
return
|
1956
|
+
|
1957
|
+
def get_vertical_position_proportion(self):
|
1958
|
+
"""
|
1959
|
+
Gives the proportion of bodies at the surface and at the bottom of the water
|
1960
|
+
"""
|
1961
|
+
|
1962
|
+
def update_pie(time_idx):
|
1963
|
+
"""Met à jour uniquement la KDE en fonction de l'index temporel sélectionné."""
|
1964
|
+
time_idx = int(time_idx) # Assurer que l'indice est un entier
|
1965
|
+
time_value = self.wanted_time[time_idx] # Temps sélectionné à afficher
|
1966
|
+
|
1967
|
+
ax.clear()
|
1968
|
+
# Mise à jour du titre pour refléter le temps sélectionné
|
1969
|
+
days = time_value // (24*3600)
|
1970
|
+
hours = (time_value % (24*3600)) // 3600
|
1971
|
+
minutes = (time_value % (3600)) // 60
|
1972
|
+
seconds = time_value % 60
|
1973
|
+
ax.set_title(f"Proportion of bodies at the bottom and surface after \n{days} days, {hours} hours, {minutes} minutes, {seconds} seconds")
|
1974
|
+
|
1975
|
+
# Obtenir les positions x, y à l'instant de temps spécifié par time_idx
|
1976
|
+
z = self.Pos_b[:,2,time_idx]
|
1977
|
+
|
1978
|
+
surface = np.where(z > 0.2)[0]
|
1979
|
+
bottom = np.where(z <= 0.2)[0]
|
1980
|
+
|
1981
|
+
counts = [len(surface)/self.newdrowning.n_b*100, len(bottom)/self.newdrowning.n_b*100]
|
1982
|
+
labels = ['Surface', 'Bottom']
|
1983
|
+
colors = [[1,0.7,0.6,1], [0.6,0.7,1,1]]
|
1984
|
+
|
1985
|
+
ax.pie(counts, labels=labels, colors=colors, autopct='%1.1f%%')
|
1986
|
+
|
1987
|
+
# Rafraîchissement du graphique
|
1988
|
+
fig.canvas.draw_idle()
|
1989
|
+
|
1990
|
+
# Initialisation de la figure et des axes
|
1991
|
+
fig, ax = plt.subplots()
|
1992
|
+
|
1993
|
+
time_idx_initial = 0
|
1994
|
+
z = self.Pos_b[:,2,time_idx_initial]
|
1995
|
+
|
1996
|
+
surface = np.where(z > 0.2)[0]
|
1997
|
+
bottom = np.where(z <= 0.2)[0]
|
1998
|
+
|
1999
|
+
counts = [len(surface)/self.newdrowning.n_b*100, len(bottom)/self.newdrowning.n_b*100]
|
2000
|
+
labels = ['Surface', 'Bottom']
|
2001
|
+
colors = [[1,0.7,0.6,1], [0.6,0.7,1,1]]
|
2002
|
+
|
2003
|
+
ax.pie(counts, labels=labels, colors=colors, autopct='%1.1f%%')
|
2004
|
+
|
2005
|
+
# Création du slider pour ajuster l'indice temporel
|
2006
|
+
ax_slider = plt.axes([0.1, 0.1, 0.8, 0.05], facecolor='lightgoldenrodyellow')
|
2007
|
+
time_slider = Slider(ax_slider, 'Time', 0, len(self.wanted_time) - 2, valinit=time_idx_initial, valstep=1)
|
2008
|
+
|
2009
|
+
# Mise à jour de la KDE lorsque le slider est utilisé
|
2010
|
+
time_slider.on_changed(update_pie)
|
2011
|
+
|
2012
|
+
plt.show()
|
2013
|
+
|
2014
|
+
|
2015
|
+
return
|
2016
|
+
|
2017
|
+
class ProgressBar(wx.Frame):
|
2018
|
+
"""
|
2019
|
+
Creates and manages the progress frame
|
2020
|
+
"""
|
2021
|
+
def __init__(self,parent, n_processes,total):
|
2022
|
+
super(ProgressBar, self).__init__(parent)
|
2023
|
+
self.n_processes = n_processes
|
2024
|
+
self.total = total
|
2025
|
+
|
2026
|
+
# Set up the main panel and sizer
|
2027
|
+
panel = wx.Panel(self)
|
2028
|
+
sizer = wx.BoxSizer(wx.VERTICAL)
|
2029
|
+
|
2030
|
+
progress_bar = wx.Gauge(panel, range=100)
|
2031
|
+
sizer.Add(progress_bar, flag=wx.EXPAND | wx.ALL, border=5)
|
2032
|
+
self.progress_bars = progress_bar
|
2033
|
+
self.progress_text = wx.StaticText(panel, label="0%")
|
2034
|
+
sizer.Add(self.progress_text, flag=wx.ALIGN_CENTER | wx.TOP, border=5)
|
2035
|
+
|
2036
|
+
panel.SetSizer(sizer)
|
2037
|
+
self.SetTitle(_("Drowning Progress"))
|
2038
|
+
self.SetSize((300, 90))
|
2039
|
+
|
2040
|
+
def update_progress(self, progress_dict):
|
2041
|
+
"""
|
2042
|
+
Update the progress bars based on the values in `progress_dict`.
|
2043
|
+
"""
|
2044
|
+
|
2045
|
+
min_progress_value = None
|
2046
|
+
for i, progress in progress_dict.items():
|
2047
|
+
# Assume progress is a percentage (0 to 100)
|
2048
|
+
if progress is not None:
|
2049
|
+
if min_progress_value is None or progress < min_progress_value:
|
2050
|
+
min_progress_value = progress
|
2051
|
+
progress_percent = int(min_progress_value/self.total*100)
|
2052
|
+
|
2053
|
+
self.progress_bars.SetValue(progress_percent)
|
2054
|
+
self.progress_text.SetLabel(f"{progress_percent}%")
|
2055
|
+
self.SetTitle(f"Drowning Progress - {progress_percent}%")
|
2056
|
+
|
2057
|
+
|
2058
|
+
class ProgressImage(wx.Frame):
|
2059
|
+
"""
|
2060
|
+
Creates and manages the progress frame with a fractioned image appearance
|
2061
|
+
"""
|
2062
|
+
def __init__(self, n_processes, total, *args, **kw):
|
2063
|
+
super(ProgressImage, self).__init__(*args, **kw)
|
2064
|
+
self.n_processes = n_processes
|
2065
|
+
total_segments = 10*n_processes
|
2066
|
+
self.total_segments = total_segments
|
2067
|
+
self.total = total
|
2068
|
+
|
2069
|
+
# Set up the main panel and sizer
|
2070
|
+
panel = wx.Panel(self)
|
2071
|
+
grid_sizer = wx.GridSizer(rows=total_segments, cols=n_processes, gap=(5, 5))
|
2072
|
+
|
2073
|
+
current_dir = Path(__file__).resolve().parent
|
2074
|
+
# Path to your main image (set it to a suitable path)
|
2075
|
+
self.image_path = str(current_dir / "image.png")
|
2076
|
+
|
2077
|
+
# Load the image
|
2078
|
+
image = wx.Image(str(self.image_path), wx.BITMAP_TYPE_PNG)
|
2079
|
+
img_width, img_height = image.GetSize()
|
2080
|
+
|
2081
|
+
# Calculate segment dimensions
|
2082
|
+
segment_height = img_height // total_segments
|
2083
|
+
segment_width = img_width // n_processes
|
2084
|
+
|
2085
|
+
# Initialize a 2D list to hold each segment for each process
|
2086
|
+
self.image_segments = []
|
2087
|
+
|
2088
|
+
# Create a grid of segments for each process
|
2089
|
+
for row in range(total_segments):
|
2090
|
+
row_segments = []
|
2091
|
+
for col in range(n_processes):
|
2092
|
+
# Extract the specific segment for each cell in the grid
|
2093
|
+
x = col * segment_width
|
2094
|
+
y = row * segment_height
|
2095
|
+
segment = image.GetSubImage((x, y, segment_width, segment_height))
|
2096
|
+
|
2097
|
+
# Create a bitmap for the segment and add it to the grid, initially hidden
|
2098
|
+
segment_bitmap = wx.StaticBitmap(panel, bitmap=wx.Bitmap(segment))
|
2099
|
+
segment_bitmap.Hide() # Hide initially; show as progress advances
|
2100
|
+
grid_sizer.Add(segment_bitmap, flag=wx.ALIGN_CENTER)
|
2101
|
+
row_segments.append(segment_bitmap)
|
2102
|
+
|
2103
|
+
self.image_segments.append(row_segments)
|
2104
|
+
|
2105
|
+
panel.SetSizer(grid_sizer)
|
2106
|
+
self.SetTitle(_("Drowning Progress"))
|
2107
|
+
self.SetSize((img_width + 50, img_height + 50))
|
2108
|
+
|
2109
|
+
def update_progress(self, progress_dict):
|
2110
|
+
"""
|
2111
|
+
Update the visibility of image segments based on the progress.
|
2112
|
+
"""
|
2113
|
+
for process_id, progress in progress_dict.items():
|
2114
|
+
if progress is not None:
|
2115
|
+
# Calculate the number of segments to show based on progress
|
2116
|
+
num_segments_to_show = int((progress / self.total) * (self.total_segments // self.n_processes))
|
2117
|
+
|
2118
|
+
# Show the segments that correspond to the current progress
|
2119
|
+
for segment_id in range(num_segments_to_show):
|
2120
|
+
self.image_segments[segment_id][process_id].Show()
|
2121
|
+
|
2122
|
+
# Refresh the layout after updating visibility
|
2123
|
+
self.Layout()
|
2124
|
+
self.Refresh()
|